Binary search tree cumsum - python

Problem: Given a binary search tree in which the keys are numbers, we'll define the operation 'cumsum' ( shorthand for cumulative sum ) that switches the key of every node in the tree with the sum of all the keys that are smaller or equal to it.
For example,
In this example,
The key 5 in the root is switched to the value 10: the sum of the original key in the root ( which is 5 ) and all smaller keys than it ( which are 2 and 3 ).
The key 3 is switched with the value 5: sum of the original key in this node ( meaning, 3 ) and all the smaller keys than it ( which is 2 ).
The key 12 in the rightmost node is switched with the value 45: sum of the original key in this node ( meaning, 12 ) and all the smaller keys than it ( which are 2,3,5,6,8 and 9 ).
Note that the method needs to be an envelope function that envelopes a recursive function. Also note that the method cumsum does not return a new tree but rather updates the tree itself ( in-place )
My attempt:
def cumsum(T):
def cumsum_rec(node,L):
L.append(node.key)
if node.left != None:
cumsum_rec(node.left,L)
if node.right != None:
cumsum_rec(node.right,L)
count = 0
for val in L:
if val < node.key:
count += val
node.key += count
L = []
cumsum_rec(T.root,L)
Explanation: I traverse each node in the tree, I save each original node in an auxiliary list denoted as 'L'. When all nodes were traversed, I look for all the nodes keys in the list 'L' that are smaller than the current node and appropriately update the key of the current node to be the sum of the keys of all nodes whose key are smaller or equal to the current node's key .
My implemenation works, for example, for the tree in the example above:
t = Binary_search_tree()
t.insert(5,'A')
t.insert(2,'A')
t.insert(9,'A')
t.insert(3,'A')
t.insert(8,'A')
t.insert(12,'A')
t.insert(6,'A')
which looks:
>>> print(t)
5
______/ |__________
2 9
/ |__ __/ |__
# 3 8 12
/ | __/ | / |
# # 6 # # #
/ |
# #
And after performing cumsum operation on it:
>>> cumsum(t)
>>> print(t)
10
______/ |____________
2 33
/ |__ __/ |__
# 5 24 45
/ | __/ | / |
# # 16 # # #
/ |
# #
My question:
Although my implementation works, I was interested in seeing other possible implementations for the sake of learning. Do you have an alternative implementation? one that doesn't require using a list as an input to the recursive function?
Addendum ( implementation of Binary search tree and Tree_node classes if you're interested ):
def printree(t, bykey = True):
"""Print a textual representation of t
bykey=True: show keys instead of values"""
#for row in trepr(t, bykey):
# print(row)
return trepr(t, bykey)
def trepr(t, bykey = False):
"""Return a list of textual representations of the levels in t
bykey=True: show keys instead of values"""
if t==None:
return ["#"]
thistr = str(t.key) if bykey else str(t.val)
return conc(trepr(t.left,bykey), thistr, trepr(t.right,bykey))
def conc(left,root,right):
"""Return a concatenation of textual represantations of
a root node, its left node, and its right node
root is a string, and left and right are lists of strings"""
lwid = len(left[-1])
rwid = len(right[-1])
rootwid = len(root)
result = [(lwid+1)*" " + root + (rwid+1)*" "]
ls = leftspace(left[0])
rs = rightspace(right[0])
result.append(ls*" " + (lwid-ls)*"_" + "/" + rootwid*" " + "|" + rs*"_" + (rwid-rs)*" ")
for i in range(max(len(left),len(right))):
row = ""
if i<len(left):
row += left[i]
else:
row += lwid*" "
row += (rootwid+2)*" "
if i<len(right):
row += right[i]
else:
row += rwid*" "
result.append(row)
return result
def leftspace(row):
"""helper for conc"""
#row is the first row of a left node
#returns the index of where the second whitespace starts
i = len(row)-1
while row[i]==" ":
i-=1
return i+1
def rightspace(row):
"""helper for conc"""
#row is the first row of a right node
#returns the index of where the first whitespace ends
i = 0
while row[i]==" ":
i+=1
return i
#######################################################################
class Tree_node():
def __init__(self, key, val):
self.key = key
self.val = val
self.left = None
self.right = None
def __repr__(self):
return "(" + str(self.key) + ":" + str(self.val) + ")"
class Binary_search_tree():
def __init__(self):
self.root = None
def __repr__(self): #no need to understand the implementation of this one
out = ""
for row in printree(self.root): #need printree.py file
out = out + row + "\n"
return out
def lookup(self, key):
''' return node with key, uses recursion '''
def lookup_rec(node, key):
if node == None:
return None
elif key == node.key:
return node
elif key < node.key:
return lookup_rec(node.left, key)
else:
return lookup_rec(node.right, key)
return lookup_rec(self.root, key)
def insert(self, key, val):
''' insert node with key,val into tree, uses recursion '''
def insert_rec(node, key, val):
if key == node.key:
node.val = val # update the val for this key
elif key < node.key:
if node.left == None:
node.left = Tree_node(key, val)
else:
insert_rec(node.left, key, val)
else: #key > node.key:
if node.right == None:
node.right = Tree_node(key, val)
else:
insert_rec(node.right, key, val)
return
if self.root == None: #empty tree
self.root = Tree_node(key, val)
else:
insert_rec(self.root, key, val)
Thanks in advance for any help!

Here's one implementation that doesn't require keeping the extra list; it just adds the numbers up as it goes.
def cumsum(T):
def cumsum_rec(node, initial):
if node is None:
return initial
left = cumsum_rec(node.left, initial)
node.key = left + node.key
right = cumsum_rec(node.right, node.key)
return right
cumsum_rec(T.root, 0)
Note that there is no need to do extra comparisons of values (my code has no <), because all of that information is already contained in the structure of the tree.

Related

Indirect recursion for number of nodes in BST is less than a given value

for this case, it ask me to do a indirect recursion. for "def count_less" function. which mean count the number of nodes which is less than the given value.For my code I will have attributeError. Can anyone help me to check my code?
class BST:
"""A Binary Search Tree."""
def __init__(self, container=[]):
"""(BST, list) -> NoneType
Initialize this BST by inserting the items from container (default [])
one by one, in the order given.
"""
# Initialize empty tree.
self.root = None
# Insert every item from container.
for item in container:
self.insert(item)
def __str__(self):
"""(BST) -> str
Return a "sideways" representation of the values in this BST, with
right subtrees above nodes above left subtrees and each value preceded
by a number of TAB characters equal to its depth.
"""
if self.root:
return self.root._str("")
else:
return ""
def count_less(self, item):
"""(BST, object) -> int
Return the number of items in this BST that are strictly less than
item.
"""
if self.root:
return self.root.count_less(item)
else:
return 0
I need to write the function body in this "_BSTNode" class, and call the class method in above "BST" class.
class _BSTNode:
"""A node in a BST."""
def __init__(self, item, left=None, right=None):
"""(_BSTNode, object, _BSTNode, _BSTNode) -> NoneType
Initialize this node to store item and have children left and right.
"""
self.item = item
self.left = left
self.right = right
def _str(self, indent):
"""(_BSTNode, str) -> str
Return a "sideways" representation of the values in the BST rooted at
this node, with right subtrees above nodes above left subtrees and each
value preceded by a number of TAB characters equal to its depth, plus
indent.
"""
if self.right:
right_str = self.right._str(indent + "\t")
else:
right_str = ""
if self.left:
left_str = self.left._str(indent + "\t")
else:
left_str = ""
return right_str + indent + str(self.item) + "\n" + left_str
def count_less(self: '_BSTNode', item: object) -> int:
"""
Return the number of items in the BST rooted at this node that are
strictly less than item.
"""
if not self.item:
return 0
elif item <= self.item:
if self.left:
return 1 + self.left.count_less(item)
return 0
elif self.item < item:
if self.left and self.right:
return 1 + self.left.count_less(item) + self.right.count_less(item)
elif self.left and not self.right:
return 1 + self.left.count_less(item)
elif self.right and not self.left:
return 1 + self.right.count_less(item)
else:
return 1
And this is my example I put in and error in my output.
>>> t = BST(container=[5,1,2,3,4,6,7,8,9])
>>> t.count_less(10)
You can depend on the falsy nature of None and use if statements to check to see if the child nodes exist. I'm pretty sure that the below code won't return the correct answer: what happens if self.item == item? It's possible for the children of both the left and right children to have items that are less than the item argument.
elif item < self.item:
if self.left:
return 1 + self.left.count_less(item)
return 1
elif item > self.item:
if self.right:
return 1 + self.right.count_less(item)
return 1

Python Trees: Modifying a tree

This is my python code to make an Ordered Binary Decision Diagram (not very relevant for the context). So I just have a tree of a particular height, and I need to set some of the leaf nodes to one. So I have a variable path which involves an array of "decisions", to go left or right from that particular node. But my code is by mistake modifying multiple roots. I am fairly new to Python and I used to rely on pointers when I used C.
def makeCubes(arr):
ans = []
for ar in arr:
ar2 = [ar[i:i + 2] for i in range(0, len(ar), 2)]
#splitting into segments of 2 each
if not '00' in ar2:
ans += [ar2]
return ans
class Node:
def __init__(self,key):
self.key = key
self.left = None
self.right = None
def addLeft(self,node):
self.left = node
def addRight(self,node):
self.right = node
def makeTree(size):
if(size == 1):
leaf = Node('x0')
leaf.addLeft(Node('zero'))
leaf.addRight(Node('zero'))
return leaf
else:
node = Node('x'+str(size-1))
childNode = makeTree(size-1)
node.addLeft(childNode)
node.addRight(childNode)
return node
def inOrder(root):
if(root != None):
return inOrder(root.left) + [root.key] + inOrder(root.right)
return []
def makeOBDD(array):
maxLen = max([len(word) for word in array])
tree = makeTree(maxLen)
for cube in array:
tree = makeOne(tree,cube)
return tree
def makeOne(root,cube):
print("cube",cube)
if(cube == []):
print("updated")
root.key = 'one'
else:
element = cube[0]
if(element == '01'):
root.addLeft(makeOne(root.left,cube[1:]))
elif(element == '10'):
root.addRight(makeOne(root.right,cube[1:]))
return root
# ab + a'b'
'''
Expected output
x1
/ \
x0 x0
/ \ / \
1 0 0 1
'''
cubeSet = ['1010','0101']
cubes = makeCubes(cubeSet)
print(cubes)
obdd = makeOBDD(cubes)
print(inOrder(obdd))

python create a binary search tree using existing function

I'm practicing creating a balanced binary search tree in python.
I already have these below, any idea on how to create a balance_bst funtion that passed a list of unique values that are
sorted in increasing order. It returns a reference to the root of a well-balanced binary search tree:
class LN:
def __init__(self,value,next=None):
self.value = value
self.next = next
def list_to_ll(l):
if l == []:
return None
front = rear = LN(l[0])
for v in l[1:]:
rear.next = LN(v)
rear = rear.next
return front
def str_ll(ll):
answer = ''
while ll != None:
answer += str(ll.value)+'->'
ll = ll.next
return answer + 'None'
# Tree Node class and helper functions (to set up problem)
class TN:
def __init__(self,value,left=None,right=None):
self.value = value
self.left = left
self.right = right
def height(atree):
if atree == None:
return -1
else:
return 1+ max(height(atree.left),height(atree.right))
def size(t):
if t == None:
return 0
else:
return 1 + size(t.left) + size(t.right)
def is_balanced(t):
if t == None:
return True
else:
return abs(size(t.left)-size(t.right)) <= 1 and is_balanced(t.left) and is_balanced(t.right)
def str_tree(atree,indent_char ='.',indent_delta=2):
def str_tree_1(indent,atree):
if atree == None:
return ''
else:
answer = ''
answer += str_tree_1(indent+indent_delta,atree.right)
answer += indent*indent_char+str(atree.value)+'\n'
answer += str_tree_1(indent+indent_delta,atree.left)
return answer
return str_tree_1(0,atree)
How do write the balance_bst?
def balance_bst(l):
Here is what I did:
def build_balanced_bst(l):
if l == None:
return None
else:
middle = len(l) // 2
return TN(l[middle],
build_balanced_bst(l[:middle]),
build_balanced_bst(l[middle + 1:]))
It gives me:
IndexError: list index out of range
How do I fix it?
I'm not going to write it for you since that's not what SO is about, but here's the general idea. Since the list is already sorted, the root should be the element in the middle of the list. Its left child will be the root of the balanced tree consisting of the elements to the left of the root in the list, and the right sub-tree will be the rest.

Binary Search Tree - Finding the size

I want to find the size of the tree with a given node which will be stated like this
print bst.get("B")
However, when I tried to print out, it keeps stating that "it only accept 1 argument but 2 is given"
Sorry, can someone help me out, as I'm quite new to this.
the brief code is:
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size(temp.left) + self.size(temp.right)
def size2(self,n):
if n is None:
return 0
else:
return 1 + self.size2(n.left) + self.size2(n.right)
The full code:
import os
import pygraphviz as pgv
from collections import deque
class BST:
root=None
def put(self, key, val):
self.root = self.put2(self.root, key, val)
def put2(self, node, key, val):
if node is None:
#key is not in tree, create node and return node to parent
return Node(key, val)
if key < node.key:
# key is in left subtree
node.left = self.put2(node.left, key, val)
elif key > node.key:
# key is in right subtree
node.right = self.put2(node.right, key, val)
else:
node.val = val
# node.count = 1 + self.size2(node.left) + self.size2(node.right)
return node
# draw the graph
def drawTree(self, filename):
# create an empty undirected graph
G=pgv.AGraph('graph myGraph {}')
# create queue for breadth first search
q = deque([self.root])
# breadth first search traversal of the tree
while len(q) <> 0:
node = q.popleft()
G.add_node(node, label=node.key+":"+str(node.val))
if node.left is not None:
# draw the left node and edge
G.add_node(node.left, label=node.left.key+":"+str(node.left.val))
G.add_edge(node, node.left)
q.append(node.left)
if node.right is not None:
# draw the right node and edge
G.add_node(node.right, label=node.right.key+":"+str(node.right.val))
G.add_edge(node, node.right)
q.append(node.right)
# render graph into PNG file
G.draw(filename,prog='dot')
os.startfile(filename)
def createTree(self):
self.put("F",6)
self.put("D",4)
self.put("C",3)
self.put("B",2)
self.put("A",1)
self.put("E",5)
self.put("I",9)
self.put("G",7)
self.put("H",8)
self.put("J",10)
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size(temp.left) + self.size(temp.right)
def size2(self,n):
if n is None:
return 0
else:
return 1 + self.size2(n.left) + self.size2(n.right)
class Node:
left = None
right = None
key = 0
val = 0
def __init__(self, key, val):
self.key = key
self.val = val
bst = BST()
bst.createTree()
bst.drawTree("demo.png")
##print bst.size("D")
I get a stack overflow with your code. I think you need to use size2 in your size method:
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size2(temp.left) + self.size2(temp.right)
Personally, I would maybe not call the method size2, but that's a matter of taste (and style). Also, the key seems to be unused?

Python Binary Tree print nodes with two exactly two children

How do I make a function that returns the number of nodes in a tree that have two children?
My class code is as follows:
class RefBinaryTree:
def __init__(self, data, left=None, right=None):
self.key = data
self.left = left
self.right = right
def insert_left(self, value):
self.left = RefBinaryTree(value, left=self.left)
def insert_right(self, value):
self.right = RefBinaryTree(value, right=self.right)
def get_left_subtree(self):
return self.left
def get_right_subtree(self):
return self.right
def set_value(self, new_value):
self.key = new_value
def get_value(self):
return self.key
def create_string(self, indent):
string = str(self.key) + '---+'
if self.left:
string += '\n(l)' + indent + self.left.create_string(indent + ' ')
if self.right:
string += '\n(r)' + indent + self.right.create_string(indent + ' ')
return string
def __str__(self):
return self.create_string(' ')
I'm guessing it would be best to use recursion. Any hints or helpful links would be awesome. Thanks.
It's really quite simple to count two-child nodes recursively. If you return a number with each function call (zero as the base case) you can simply add 1 every time you find a two-child node:
def findDoubleNodes(tree):
if tree == None or (tree.left == None and tree.right == None):
# base case
return 0
elif tree.left <> None and tree.right <> None:
# both have children, so add one to our total and go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right) + 1
else:
# only one child, so only go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right)
Inputting a RefBinaryTree returns the number of nodes with two children. An example:
x = RefBinaryTree(1)
x.insert_left(5)
x.left.insert_left(6)
x.left.insert_right(7)
x.left.right.insert_left(8)
x.left.right.insert_right(9)
x.left.right.right.insert_right(10)
The (lazily) created tree looks like this:
1
/
5
/ \
6 7
/ \
8 9
\
10
And findDoubleNodes(x) returns 2, as only two nodes (5 and 7) have two children.
Additionally, adding a left child to node 9 (x.left.right.right.insert_left(11)) has the expected result, returning 3.
This should do:
def countNodes(tree):
if tree is None:
return 0
left = tree.get_left_subtree()
rght = tree.get_right_subtree()
return (0 if left is None or rght is None else 1) \
+ countNodes(left) + countNodes(rght)

Categories