This is the code I came up with to insert a new value into a BST:
class BST(object):
def __init__(self, root):
self.root = Node(root)
def insert(self, new_val):
self.__internal_insert(self.root, new_val)
def __internal_insert(self, node, new_val):
if node is None:
node = Node(new_val)
elif new_val < node.value:
self.__internal_insert(node.left, new_val)
else:
self.__internal_insert(node.right, new_val)
# Set up the tree
tree = BST(4)
# Insert elements
tree.insert(2)
tree.insert(1)
tree.insert(3)
tree.insert(5)
however, while debugging I noticed that the self.root is never updated, eg.: as soon as the __internal_insert() method finishes and a new insert() is performed, its sibling nodes left and right are back to None instead to the previous value that was set.
Hope you help me spot where the bug is. I picked up Python recently, my apologizes if this is a trivial question.
You defined node = Node(new_val), but then where do you ever attach that node to it's parent?
Basically, you recurse, but never captured the results you're building
Try returning the node you created
def __internal_insert(self, node, new_val):
if node is None:
node = Node(new_val)
elif new_val < node.value:
node.left = self.__internal_insert(node.left, new_val)
else:
node.right = self.__internal_insert(node.right, new_val)
return node
I see two issues with the code.
First, when you reassign node, it is not assigning the value to the BST object. To do that, you need to reference it directly: self.left = Node(new_val).
Second, you're not checking the equals condition, so your code isn't going to work like a traditional BST. You'll end up with a lot of extraneous nodes.
Related
I'm trying to add elements to a binary tree and print them in in-order.
I'm getting an error while adding an element: AttributeError: 'NoneType' object has no attribute 'left'
Please let me know where I have to make a change Below is the code
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.data = data
def insert(self, data):
if self.data:
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.data = data
def InorderTraversal(self):
if self.data is None:
return
self.left.InorderTraversal()
print(self.data)
self.right.InorderTraversal()
if __name__ == "__main__":
root = Node(1)
root.insert(2)
root.insert(3)
root.insert(4)
root.InorderTraversal()
I am Implementing Trees First time Doesn't Have any idea
You should check if a node has left & right children before recursing on them:
class Node:
...
def InorderTraversal(self):
if self.left: # if left child exists, then recurse on it
self.left.InorderTraversal()
print(self.data)
if self.right: # if right child exists, then recurse on it
self.right.InorderTraversal()
Result:
1
2
3
4
You need the Node to be a separate class which will be used by the Tree class.
The insert and inorder_traversals are operations you do on a tree and not on a node. This is kinda the first thing you should do.
There are already pretty good resources you can look at since I might not be able to explain in that kind of detail :
https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
Also, you need to read up a little on how recursion and the classes work in python as I see some mistakes that can be fixed.
You're recursively printing the inorder traversal but you're doing the checks on self.data and running on self.left and self.right . self is pointing to the same data and you need to pass the node you wanna process next in the recursive function.
Also, you need to check the None condition on the Node and not on the data. Basically the node doesn't exist and that means you've to return from there
The inorder code overall idea is correct but you're working with wrong variables and basically you'll end up with incorrect output (or infinite loop)
My suggestion is to read up some more on tree on geeksforgeeks to begin with!
I created a small function funct which should assign None to node if a value of -1 is passed else assign the value to the value attribute of the node object. I created a simple binary tree [root, left, right] and finally print the nodes.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def funct(node, val):
if val == -1:
### modify code here
node = None
else:
node.val = val
root = TreeNode()
root.val = 'root'
root.left = TreeNode()
root.right = TreeNode()
funct(root.left, 'left')
funct(root.left, -1)
print(root, root.val)
print(root.left, root.left.val)
print(root.right, root.right.val)
When I print the nodes I see the following output.
The right node is in memory and is not None.
How do I assign None to the orignal object by modifying the code inside the if in funct to get the following output instead.
esentially simulating the following code and getting the output :
root = TreeNode()
root.val = 'root'
root.left = TreeNode('left')
root.right = None
Note : I can change my algo. to create a new node only when val != -1. However I want to understand if there is a way to modify a passed object it in pyhton.
Edits : removed the word "delete". Added some clarification.
I created a small function funct which should assign None to node if a value of -1 is passed else assign the value to the value attribute of the node object.
funct cannot replace the node that was passed to it (i.e.: make one of the caller's variables refer to a new node instead of this one), because it receives the node, and not any variable name for that node.
It can modify the node that was passed to it. In particular, it can replace the child values, by reassigning to node.right and node.left. This means "the .right and .left of the TreeNode instance that was passed to me". node is a name that funct uses for that passed-in instance, that has nothing to do with the calling code in any way.
To "remove" a subtree, then, we need to pass the parent of the tree that will be removed, and also indicate which side will be removed. That might look like:
def remove_child(parent, which):
if which == 'left':
parent.left = None
elif which == 'right':
parent.right = None
else:
raise ValueError("invalid child name")
The root of the tree has no parent, so removing the entire tree must be treated as a special case.
Not sure if this is what you need, but I think its doing the behavior you want:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def funct(self, child: str, val):
if val == -1:
### modify code here
setattr(self,child, None)
else:
setattr(self,child,TreeNode(val))
root = TreeNode()
root.val = 'root'
root.left = TreeNode()
root.right = TreeNode()
root.funct(child='left', val='left')
root.funct(child='right', val='right')
print(root, root.val)
print(root.left, root.left.val)
print(root.right, root.right.val)
print(None)
root.funct(child='left', val= -1)
print("\n")
print(root, root.val)
# print(root.left, root.left.val) - you cannot print root.left.val because None has no attribute
print(root.left)
print(root.right, root.right.val)
I have a binary search tree. I have written basic insert, delete, traversal of the full tree and find its maximum and minimum node of the tree but I have trouble with finding maximum and minimum node after deleting the minimum or the maximum node.
This function deletes a specific node:
def deleteNode(self,val):
if self.data:
if self.data > val:
self.left.deleteNode(val)
elif self.data < val:
self.right.deleteNode(val)
else:
if self.left is None:
self.data = self.right
return True
elif self.right is None:
self.data = self.left
return True
else:
dltNode = self
dltNode.data = self.data
largest = self.left.findMax()
dltNode.data = largest
dltNode.left.deleteNode(largest)
return True
This function finds the minimum node:
def findMin(self):
if self.data:
if self.left is None:
return self.data
else:
return self.left.findMin()
And this is for the maximum node:
def findMax(self):
if self.data:
if self.right is None:
return self.data
else:
return self.right.findMax()
findMin and findMax functions work fine without deleting any node.
If I ever call then after deleting the minimum and maximum node they will return None, whether they were supposed to return only integer node data. Here is the screenshot of my output:
It should print 34 instead of None.
Here is my full code
There are a few issues in deleteNode
if self.data should not be there: this would mean you cannot delete a node with value 0 from the tree. A similar problem exists in other methods (findMin, findMax, insert, ...).
self.left.deleteNode(val) is executed without checking first that self.left is not None. The same is true for self.right.deleteNode(val)
self.data = self.right assigns a Node reference to a data attribute (which is supposed to be a number).
The function sometimes returns nothing (None) or True. Because of the recursive nature, the original caller of the method will get None. This is not consistent.
The function cannot deal with deleting the root Node, as the caller will keep using the root node reference (t or t2 in your example code).
To solve these issues, you should either create a separate Tree class, or agree that deleteNode returns the root node of the tree, which could be a different root than the root on which the call was made (when the root node was deleted), or even None when that was the last node of the tree.
Here is how that would look:
def deleteNode(self,val):
if self.data > val:
if self.left:
self.left = self.left.deleteNode(val)
elif self.data < val:
if self.right:
self.right = self.right.deleteNode(val)
else:
if self.left is None:
return self.right
elif self.right is None:
return self.left
else:
largest = self.left.findMax()
self.data = largest
self.left = self.left.deleteNode(largest)
return self
To do it right, you would need to use the return value from this method, for example:
t1 = t1.deleteNode(34)
NB: In some methods you check whether self.data is None. I understand this is some special condition for indicating that the root node is not really a node, and should be considered an empty tree, but this is not a nice pattern. Instead, an empty tree should be just None, or you should define another Tree class which has a root attribute which can be None.
I'm trying to implement a binary tree with insert and preorder methods.
After adding elements to the tree, only one element is displayed.
Can someone let me know where I'm wrong.
Below is code:
class Node(object):
def __init__(self, value, left=None, right=None):
self.value = value
self.left = None
self.right = None
def __repr__(self):
return '{}'.format(self.value)
class BinaryTree(object):
def __init__(self, root=None):
self.root = root
def add(self, value):
val = self.root
if not val:
self.root = value
val = value
elif not val.left:
val = value
elif not val.right:
val = value
else:
self.left = val.left.add(value)
return val
def preorder(self):
val = self.root
if not val: # this will handle the case when root node is None.
return
print(val)
if val.left:
val.left.preorder()
if val.right:
val.right.preorder()
def main():
binary_tree = BinaryTree()
print("Adding nodes to the tree")
for i in range(1, 11):
node = Node(i)
binary_tree.add(node)
print("Printing preorder...")
binary_tree.preorder()
if __name__ == '__main__':
main()
Output
Adding nodes to the tree
Printing preorder...
1
Your code has a few different errors. Some relate to how you modify self.root (or fail to), others have to do with attempts at recursion on the wrong types.
The first issue, which is why your code fails silently, has to do with your BinaryTree.add method, which does nothing when the tree is empty. The problem is that you initialize a local variable val to be equal to your root node (if you have one), and then later rebind it to some other value. But that never changes the root value at all, only the local val variable.
I suggest you get rid of val all together, and instead read and write self.root directly. Then you'll actually make some progress, and see the other issues.
Here's a start:
def add(self, value):
if self.root is None:
self.root = value
elif self.root.left.left is None:
self.root.left = value
...
The other issues I mention are both similar, though one occurs in BinaryTree.add and the other in BinaryTree.preorder. The issue is that you try to call the same method (add or preorder) on one of the children of your root node. But the nodes are Node instances, and don't have the methods that you've defined in the BinaryTree class.
This issue doesn't have as obvious a solution as the previous one. One idea might be to move the logic for the methods into the Node class (where you can recurse easily), and leave only the empty-tree handling code in the BinaryTree methods (everything else gets delegated to the root node).
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
class BST:
def __init__(self, root=None):
self.root = root
def remove(self, val):
if self.root == None:
return root
else:
self._remove(val, self.root)
def _remove(self, val, node):
if node == None:
return node # Item not found
if val < node.val:
self._remove(val, node.left)
elif val > node.val:
self._remove(val, node.right)
else:
# FOUND NODE TO REMOVE
if node.left != None and node.right != None: # IF TWO CHILDREN
node.val = self._find_min(node.right)
node.right = self._remove(node.val, node.right)
else: # ZERO OR ONE CHILD
if node.left == None: # COVERS ZERO CHILD CASE
node = node.right
elif node.right == None:
node = node.left
return node
Cannot figure out why this function will not delete some values. I debugged with print statements and can see that if I try to remove a value, the function will enter the else block and successfully remove a node with two children. However, when attempting to remove a node with one or zero children, the codes executes with no errors, but when I print the tree to view its contents the node is still there.
The node to be removed will have at least one None child, and it seems straightforward to set the node equal to its right (or left) child, which I assume sets the node to None.
I have some experience with Java, but fairly new to Python, and I sometimes run into trouble with the "self" protocol, but I don't think that is the case here.