RecursionError: maximum recursion depth exceeded - Binary Tree - python

While implementing add_node and search methods for a binary tree, Im getting a RecursionError: maximum recursion depth exceeded
Code:
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
class BinaryTree:
def __init__(self, root=None):
self.root = root
def add_node(self, node, value):
node = self.root
if node is not None:
if not node.value:
node.value = value
elif not node.left:
node.left = value
elif not node.right:
node.right = value
else:
node.left = self.add_node(node.left, value)
else:
self.root = TreeNode(value)
def search(self, value):
node = self.root
found = False
while node is not None:
if node.value == value:
found = True
if node.left:
found = node.left.search(value)
if node.right:
found = found or node.left.search(value)
return found
def main():
binary_tree = BinaryTree()
binary_tree.add_node(binary_tree.root, 200)
binary_tree.add_node(binary_tree.root, 300)
binary_tree.add_node(binary_tree.root, 100)
binary_tree.add_node(binary_tree.root, 30)
binary_tree.traverse_inorder(binary_tree.root)
print(binary_tree.search(200))
if __name__ == '__main__':
main()
Error:
Traceback (most recent call last):
File ".\binary_tree_test20.py", line 51, in <module>
main()
File ".\binary_tree_test20.py", line 45, in main
binary_tree.add_node(binary_tree.root, 30)
File ".\binary_tree_test20.py", line 22, in add_node
node.left = self.add_node(node.left, value)
File ".\binary_tree_test20.py", line 22, in add_node
node.left = self.add_node(node.left, value)
File ".\binary_tree_test20.py", line 22, in add_node
node.left = self.add_node(node.left, value)
[Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded

This is a remedy I can give you.
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def _add_node(node, value):
if not node.value:
node.value = value
elif not node.left:
node.left = TreeNode(value)
elif not node.right:
node.right = TreeNode(value)
else:
_add_node(node.left, value)
class BinaryTree:
# ...
def add_node(self, value):
node = self.root
if node is not None:
_add_node(node, value)
else:
self.root = TreeNode(value)
# ...
def main():
binary_tree = BinaryTree()
binary_tree.add_node(200)
binary_tree.add_node(300)
binary_tree.add_node(100)
binary_tree.add_node(30)
Although I recommend only extending TreeNode definition without defining BinaryTree.

You're getting infinite recursion because you're not using the node parameter, you're replacing it with self.root. So when you recurse, you start at the root again each time, and never end.
Also, the line node.left = self.add_node(node.left, value) expects add_node to return the new node, but your method doesn't return anything. When it's updating an existing node it should just return the modified node; if it's creating a new node, it returns that node.
def add_node(self, node, value):
if node is not None:
if not node.value:
node.value = value
elif not node.left:
node.left = value
elif not node.right:
node.right = value
else:
node.left = self.add_node(node.left, value)
return node
else:
return TreeNode(value)
You would call this method like this:
binary_tree.root = binary_tree.add_node(binary_tree.root, 30)

Related

Why do I get RecursionError in this implementation of sorted dictionary as Binary Search Tree?

I am trying to implement a kind of sorted dictionary as a Binary Search Tree. The idea is that no matter what operation I do on this new dictionary-like object, the elements of the dictionary are always sorted with respect to the keys. My implementation is not complete - there are some issues of performance that I would like to fix before completing the code.
The idea is to create two classes, Nodeand SortedDict().
The class Node has the __getitem__ and __setitem__ methods to insert and get elements in the tree (for the moment I am not implementing a delete operation).
The class SortedDict() takes a sorted dictionary (for the moment I am not implementing cases where the argument is not a sorted dictionary), and it starts inserting the elements of the dictionary starting from the leaves of the tree. First, it processes the left child until it reaches the median of the keys of the dictionary. Then, it processes the right child, and then it glues the two children trees to the root. The root is the median of the sorted dictionary' keys.
If the dictionary is very large, I get a RecursionError: maximum recursion depth exceeded in comparison error if I try to access an element that is very far from the root. Why do I get this error? How can I avoid it?
The traceback is:
Traceback (most recent call last):
File "main.py", line 96, in <module>
print(dic_obj[5])
File "main.py", line 91, in __getitem__
return self.root.__getitem__(item)
File "main.py", line 26, in __getitem__
return self.left.__getitem__(item)
File "main.py", line 26, in __getitem__
return self.left.__getitem__(item)
File "main.py", line 26, in __getitem__
return self.left.__getitem__(item)
[Previous line repeated 994 more times]
File "main.py", line 18, in __getitem__
if item > self.root:
RecursionError: maximum recursion depth exceeded in comparison
To reproduce this error you can use the following code:
dic = {k:k for k in range(1,10000)}
dic_obj = SortedDict(dic)
print(dic_obj[5])
where the definition of SortedDict is given as follow:
class Node():
def __init__(self, root=None, value=None, left=None, right=None, parent=None):
self.parent = parent
self.root, self.value = root, value
self.left = left
self.right = right
def __str__(self):
return f'<Node Object - Root: {self.root}, Value: {self.value}, Parent: {self.parent}>'
def __getitem__(self, item):
if self.root is None:
raise KeyError(item)
if item > self.root:
if self.right is None:
raise KeyError(item)
return self.right.__getitem__(item)
if item < self.root:
if self.left is None:
raise KeyError(item)
return self.left.__getitem__(item)
if item == self.root:
return self.value
def __setitem__(self, key, value):
if self.root is None:
self.root, self.value = key, value
else:
if key > self.root:
if self.right is not None:
self.right.__setitem__(key,value)
else:
self.right = Node(root=key, value=value)
self.right.parent = self
elif key < self.root:
if self.left is not None:
self.left.__setitem__(key,value)
else:
self.left = Node(root=key, value=value)
self.left.parent = self
elif key == self.root:
self.root = value
class SortedDict():
def __init__(self, array: dict):
self.root = Node()
if array:
keys = list(array.keys())
for key in range(len(keys)//2):
self.__setitem__(keys[key],array[keys[key]])
root = Node(root=keys[key+1],value=array[keys[key+1]])
self.root.parent = root
root.left = self.root
self.root = Node()
for key in range(len(keys)//2+1,len(keys)):
self.__setitem__(keys[key],array[keys[key]])
self.root.parent = root
root.right = self.root
self.root = root
def __setitem__(self, key, value):
try:
if key > self.root.root:
if self.root.right is None and self.root.left is None:
node = Node(root=key, value=value)
self.root.parent = node
node.left = self.root
self.root = node
else:
if self.root.right is None:
self.root.right = Node(root=key, value=value, parent=self.root)
else:
node = Node(root=key, value=value)
self.root.parent = node
node.left = self.root
self.root = node
except:
self.root.root = key
self.root.value = value
def __getitem__(self, item):
return self.root.__getitem__(item)

Delete a segment from BST in Python

first post here. I am supposed to build a BST (which I have done) and create a deleteNode function. I keep trying to run the function but it is unfortunately not working.
#deleteFunction#
def deleteNode(self, data):
if self is None: ##none is left and right val
return self
if data < self.data: #if input is less than current
self.left = self.deleteNode(self.left, data) #go to the left node
elif (data > self.data): #if input is higher, go to right node
self.right = self.deleteNode(self.right, data)
else:
if self.left is None:
temp = self.right #if left is none then assign temp to right
self.left = None
return temp
elif self.right is None: #if right is none, assign temp to left
temp = self.left
self.left = None
return temp
temp = findMinNode(self.right) ##node with two children, get the smallest right subtree
self.data = temp.data ##copy the right small subtree
self.right = deleteNode(self.right, temp.data) #delete smallest right subtree
return self
##Execution code:##
print("Maximum node in BT: \n", dTree.findMaxNode())
print("Minimum node in BT: \n",dTree.findMinNode())
print("Post Order: \n", dTree.postOrderTrav())
print("Pre Order: \n", dTree.preOrderTrav())
print("In Order: \n", dTree.inOrderTrav())
dTree.deleteNode(4)
print("deletion of one node: ")
print (dTree.inOrderTrav())
I keep receiving the following error:
line 262, in <module> dTree.deleteNode(4)
File "C:/Users", line 215, in deleteNode self.right = self.deleteNode(self.right, data)
TypeError: deleteNode() takes 2 positional arguments but 3 were given
200
This is my favorite version of deleting a node in BST - use deleteNode function, with root being the root node and key being the node value that you want to delete.
class DeletingNodeBST:
def successor(self, root):
root = root.right
while root.left:
root = root.left
return root.val
def predecessor(self, root):
root = root.left
while root.right:
root = root.right
return root.val
def deleteNode(self, root, key):
if not root:
return None
if key > root.val:
root.right = self.deleteNode(root.right, key)
elif key < root.val:
root.left = self.deleteNode(root.left, key)
else:
if not (root.left or root.right):
root = None
elif root.right:
root.val = self.successor(root)
root.right = self.deleteNode(root.right, root.val)
else:
root.val = self.predecessor(root)
root.left = self.deleteNode(root.left, root.val)
return root
Note that root is the root node, which can be created with:
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None

I'm trying to implement Binary Tree insert method with recursive calls

I'm trying to implement Binary Tree insert method with recursive calls. When i run insert function it gives me an AtributeError : BinTree instance has no atribute 'root. Can you tell me pls how I can fix it??
from random import randint
class Node:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
class BinTree:
def __init___(self):
self.root = None
def insert(self, value):
if self.root == None:
self.root = Node(value)
else:
self._insert(value, self.root)
def _insert(self, value, curr_node):
if value < curr_node.value:
if curr_node.left == None:
curr_node.left = Node(value)
else:
self._insert(value, curr_node.left)
elif value>curr_node.value:
if curr_node.right == None:
curr_node.right = Node(value)
else:
self._insert(value, curr_node.rigth)
else:
print('Node already exist!')
def printTree(self):
if self.root != None:
self._printTree(self.root)
def _printTree(self, curr_node):
if curr_node !=None:
self._printTree(curr_node.left)
print(str(curr_node.val))
self._printTree(curr_node.right)
def fillTree(tree):
for _ in range(100):
curr_elem = randint(0,50)
tree.insert(curr_elem)
return tree
tree = BinTree()
fillTree(tree)
tree.printTree()
Your __init__ has a typo in the name. There are 3 trailing underscores. Change it to:
...
class BinTree:
def __init__(self):
self.root = None
And your code should work
Edit: found a couple of more issues. Here's the complete fixed code:
from random import randint
class Node:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
class BinTree:
def __init__(self):
self.root = None
def insert(self, value):
if self.root == None:
self.root = Node(value)
else:
self._insert(value, self.root)
def _insert(self, value, curr_node):
if value < curr_node.value:
if curr_node.left == None:
curr_node.left = Node(value)
else:
self._insert(value, curr_node.left)
elif value>curr_node.value:
if curr_node.right == None:
curr_node.right = Node(value)
else:
self._insert(value, curr_node.right)
else:
print('Node already exist!')
def printTree(self):
if self.root != None:
self._printTree(self.root)
def _printTree(self, curr_node):
if curr_node !=None:
self._printTree(curr_node.left)
print(str(curr_node.value))
self._printTree(curr_node.right)
def fillTree(tree):
for _ in range(100):
curr_elem = randint(0,50)
tree.insert(curr_elem)
return tree
tree = BinTree()
fillTree(tree)
tree.printTree()
Only BinTrees have roots; the children of a BinTree are Nodes, not BinTrees and thus don't have roots.

recursive insert only inserts first element python bst

I'm trying to implement insert function of bst but my recursive approach only inserts the first value. My approach was to traverse until the empty child and set that node to new value.
class BSTNode(object):
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def insert(self, val):
if self.root is None:
self.root = BSTNode(val)
else:
self.insertNode(self.root,val)
def insertNode(self,node, val):
if node is None:
node = BSTNode(val)
return
elif val <= node.val:
node.left = self.insertNode(node.left, val)
elif val > node.val:
node.right = self.insertNode(node.right, val)
bst = new BST()
bst.insert(5)
bst.insert(10)
You're not returning BSTNode in insertNode (so you create a node but return None). You should do this instead:
if node is None:
return BSTNode(val)
So the full method becomes:
def insertNode(self, node, val):
if node is None:
return BSTNode(val)
elif val <= node.val:
node.left = self.insertNode(node.left, val)
elif val > node.val:
node.right = self.insertNode(node.right, val)
return node
It might be helpful to first create a node class... it's easier to think of a node alone than it is to think about a Tree.
class Node:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
def insert_value(self,value):
# there are 3 cases
if not self.value: # in one case this node has no value
self.value = value
elif value < self.value: # if value is less than this nodes value
# then insert it to the left
if not self.left:
self.left = Node(value)
else:
self.left.insert_value(value)
else: # otherwise if value is greater than or equal to this nodes value
# then insert it to the right
if not self.right:
self.right = Node(value)
else:
self.right.insert_value(value)
Once you have this Node class the tree implementation becomes trivial.
class BST:
def __init__(self,root_value=None):
self.root = Node(root_value)
def insert_value(self,value):
self.root.insert_value(value)
bst = BST()
bst.insert_value(5)
bst.insert_value(10)
print(bst.root)
Now you certainly do not have to solve this in this fashion ... but it makes it easier to reason about (at least for me).

BST insertion function in Python

I am implementing BST in python and having some trouble on insertion function.
class Node:
def __init__(self, val):
self.data = val
self.Leftchild = self.Rightchild = None
class Tree:
def __init__(self):
self.root = None
def insert(self, val):
if self.root is None:
self.root = Node(val)
return self.root
else:
if self.root.data <= val:
self.root.Rightchild = self.insert(self.root.Rightchild, val)
else:
self.root.Leftchild = self.insert(self.root.Leftchild, val)
return self.root
if __name__ == '__main__':
tree = Tree()
for i in range(10):
tree.insert(random.randint(0,100))
I got TypeError on recursive.
TypeError: insert() takes 2 positional arguments but 3 were given
Isn't self.root.Rightchild or self.root.Leftchild considered same as self?
If my thought is wrong, how can I implement recursive insertion function in this case?
Thanks in advance!
You should have insert take another argument, root, and do your operations on that. You'll need to modify the recursive logic too. Have insert work exclusively with the Node data.
You should handle cases where an item already exists. You don't want duplicates put into the tree.
In the recursive case, you are calling insert on the wrong object.
Also, the two are not the same. self refers to the current Tree object, and self.root refers to the current Tree's Node object, and so on.
This is how you'd modify your function:
def insert(self, root, val):
if root is None:
return Node(val)
else:
if root.data <= val:
root.Rightchild = self.insert(root.Rightchild, val)
else:
root.Leftchild = self.insert(root.Leftchild, val)
return root
Try this one if you need class and its instance
import random
class Node:
def __init__(self, val):
self.data = val
self.Leftchild = self.Rightchild = None
class Tree:
def insert(self,root, val):
if root is None:
root = Node(val)
return root
else:
if root.data <= val:
root.Rightchild = self.insert(root.Rightchild, val)
else:
root.Leftchild = self.insert(root.Leftchild, val)
return root
def inorder(self, root):
if root:
self.inorder(root.Leftchild)
print root.data,
self.inorder(root.Rightchild)
if __name__ == '__main__':
tree = Tree()
root = None
for i in range(10):
root = tree.insert(root, random.randint(0,100))
tree.inorder(root)
Try this..
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def inorder(root):
if root:
inorder(root.left)
arr.append(root.data)
print root.data,
inorder(root.right)
def insert(root, data):
node = Node(data)
if root is None:
root = node
elif root.data >= data:
if root.left is None:
root.left = node
else:
insert(root.left, data)
else:
if root.right is None:
root.right = node
else:
insert(root.right, data)
if __name__ == '__main__':
root = Node(50)
insert(root, 30)
insert(root, 20)
insert(root, 40)
insert(root, 70)
insert(root, 60)
insert(root, 80)
inorder(root)

Categories

Resources