Removing a node from binary tree - python

I'm currently working on leetcode problem 366 where we have to find list of lists that contains values of leaves of each generation. I wanted to achieve this by recursion where if a node does not have left or right child, the value is recorded then the node removed by setting it to None. Here is my code:
def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
leaf_list = []
sub_list = []
def traverse(node):
if node == None:
return
if node.left == None and node.right == None:
sub_list.append(node.val)
node = None
return
traverse(node.left)
traverse(node.right)
return root
while True:
if root == None:
break
sub_list = []
traverse(root)
leaf_list.append(sub_list)
print(leaf_list)
return leaf_list
The problem seems to be that when a certain node is set to None, that change isn't retained. Why is it that I can't set a node to None to remove it?
Thanks

The tree can only be mutated when you assign to one if its node's attributes. An assignment to a variable, only changes what the variable represents. Such assignment never impacts whatever previous value that variable had. Assigning to a variable is like switching from one value to another without affecting any data structure. So you need to adapt your code such that the assignment of None is done to a left or right attribute.
The exception is for the root node itself. When the root is a leaf, then there is no parent to mutate. You will then just discard the tree and switch to an empty one (None).
One way to achieve this, is to use the return value of traverse to update the child-reference (left or right) that the caller of traverse needs to update.
Here is your code with those adaptations:
def findLeaves(root):
sub_list = []
def traverse(node):
if not node:
return
if not node.left and not node.right:
sub_list.append(node.val)
return # By returning None, the parent can remove it
node.left = traverse(node.left) # Assign the returned node reference
node.right = traverse(node.right)
return node # Return the node (parent does not need to remove it)
leaf_list = []
while root:
sub_list = []
root = traverse(root)
leaf_list.append(sub_list)
return leaf_list

Related

how does the linked list goes to the second node

so I am learning linked lists in python, but I didn't understand how does the linked list goes to the second node as it assigns the next node as the root node, please explain it to me, thanks.
the code
class Node
def __init__(self,d,n=None,p=None):
self.data=d
self.next_node=n
self.previous_node=p
def __str__(self):
return ('(' + str(self.data) + ')')
class linked_list:
def __init__(self,r=None):
self.root=r
self.size=0
def add(self,d):
new_node=Node(d,self.root)#here i didn't understand how we assign the next node
self.root=new_node
self.size +=1
def find(self,d):
this_node=self.root
while this_node is not None:
if this_node.data==d:
print(this_node.next_node)
return this_node.data
else:
this_node = this_node.next_node
return None
def remove(self,d):
this_node=self.root
previouse_node=None
while this_node is not None:
if this_node.data==d:
if previouse_node is not None:
previouse_node.next_node=this_node.next_node
else:
self.root=this_node.next_node
self.size -=1
else:
previouse_node=this_node
this_node=this_node.next_node
return False
def print_list(self):
this_node = self.root
while this_node is not None:
print(this_node, end='->')
this_node = this_node.next_node
print('None')
l_list=linked_list()
l_list.add('4')
l_list.add('40')
l_list.add('5')
l_list.print_list()
#////////////////////////////////////////////////////////////////////////////////////
In the add function we create a new node that will become our new root aka our new first element. Therefore we first assign the current root as the next_node of our new node. Afterwards we make our new node the new root.
The linked_list.root attribute actually acts like a tail. When a new node is added, it becomes the next_node of the original root and the root is assigned with that new node (making its content a tail, not a root).
Also, the Node class suggests a doubly linked list but its previous_node value is not properly assigned by linked_list.add.
If that is not your code, it is not a good example to learn from. If it is, then it needs more work and you should probably draw on paper the links that should result from adding a node to the list.
For example:
linked_list.root
|
v
None <-- node1 --> None
linked_list.add(node2) ...
What should happen:
linked_list.root
|
v
None <-- node1 --+
^ |
| v
+--- node2 --> None
What actually happens:
linked_list.root # points to the last node added (i.e. tail)
|
v
None <-- node2 --> None # missing link to node1
^
|
None <-- node1 --+

How to fix NoneType error in Python Binary Search Tree?

I'm creating a basic binary search tree program where the nodes have to be strings however, I keep getting this error:
'builtins.AttributeError: 'NoneType' object has no attribute 'addNode'
I'm a bit confused because I thought you had to declare the children nodes as None. My code is below (please excuse the messiness and extra print statements):
class BinarySearchTree:
#constructor with insertion value & left/right nodes as None
def __init__(self, data):
self.data = data
self.left = None
self.right = None
#function to insert node
def addNode(root, data):
#when tree doesn't exist
if root == None:
return BinarySearchTree(data)
else:
#when node is already in tree
if root.data == data:
return root
#smaller values go left
elif data < root.data:
root.left.addNode(data)
#bigger values go right
else:
root.right.addNode(data)
return root
#function to find smallest node value (to help with deletion)
def smallestNode(root):
node = root
#loop goes to lowest left leaf
while(node.left is not None):
node = node.left
return node
#function to delete node
def removeNode(root, data):
if root == None:
return root
#when node to be deleted in smaller than root, go left
if data < root.data:
root.left = root.left.removeNode(data)
#when node to be deleted in bigger than root, go right
elif data > root.data:
root.right = root.right.removeNode(data)
##when node to be deleted in the same as root...
else:
#when node has only 1 or 0 children
if root.right == None:
move = root.left
root = None
return move
elif root.left == None:
move = root.right
root = None
return move
#when node has 2 children, copy then delete smallest node
move = root.right.smallestNode()
root.data = move.data
root.right = root.right.removeNode(move.data)
return root
def findNode(root, data):
#if current node is equal to data value then return the root
if root.data == data or root == None:
return root
#if current node is greater than the data value then, search to the left
elif data < root.data:
return root.left.findNode(data)
#if current node is less than the data value then, search to the right
else:
return root.right.findNode(data)
def createBST(keys):
root = BinarySearchTree(keys[0])
for i in range(1,len(keys)):
root.addNode(keys[i])
return root
print('Hi! Welcome to the Binary Search Tree Builder')
print('Here are your options below:')
print('1) Build new tree')
print('2) Add a node')
print('3) Find a node')
print('4) Delete a node')
choice = int(input('What would you like to do?: '))
if choice == 1:
nodes = list(input("Enter the strings you would like to build your tree from (separate by a space): ").split())
print(nodes)
tree = createBST(nodes)
print(tree)
I'm wondering where exactly is this error coming from and how can I fix it? Also, if you see any other problems occuring in my code, please let me know!
You have conflated the notion of "tree node" and "tree". What you have there is a mixture of the two. Remember, the first parameter to ALL member functions should be "self". Replacing "root" by "self" might make your problem more clear.
So, you might create a BinaryTree class, which has a single member called self.root that holds a BinaryTreeNode object, once you have a node. The nodes will hold the left and right values, each of which will either have None or a BinaryTreeNode object. The node class probably doesn't need much code -- just self.left and self.right.
The BinaryTree class will have an addNode member that knows how to traverse the tree to find the right spot, but when you find the spot, you just set self.left = BinaryTreeNode(...). A node does not know about the tree, so a node does not know how to add new nodes. That's a function of the tree itself.
Does that make your path forward more clear?

Binary Tree: How Do Class Instances Link?

I am trying to understand binary trees, but doing so has brought me to confusion about how class instances interact, how does each instance link to another?
My Implementation:
class Node(object):
def __init__(self, key):
self.key= key
self.L = None
self.R = None
class BinaryTree(object):
def __init__(self):
self.root = None
def get_root(self):
return self.root
def insert(self, key):
if self.get_root()==None:
self.root = Node(key)
else:
self._insert(key, self.root)
def _insert(self, key, node):
if key < node.key:
if node.L == None:
node.L = key
else:
self._insert(key, Node(node.L))
if key > node.key:
if node.R == None:
node.R = key
else:
self._insert(key, Node(node.R))
myTree= BinaryTree()
A Scenario
So lets say I want to insert 10, I do myTree.insert(10) and this will instantiate a new instance of Node(), this is clear to me.
Now I want to add 11, I would expect this to become the right node of the root node; i.e it will be stored in the attribute R of the root node Node().
Now here comes the part I don't understand. When I add 12, it should become the child of the root nodes right child. In my code this creates a new instance of Node() where 11 should the be key and 12 should be R.
So my question is 2-fold: what happens to the last instance of Node()? Is it deleted if not how do I access it?
Or is the structure of a binary tree to abstract to think of each Node() connected together like in a graph
NB: this implementation is heavily derived from djra's implementation from this question How to Implement a Binary Tree?
Make L and R Nodes instead of ints. You can do this by changing the parts of your _insert function from this:
if node.L == None:
node.L = key
to this:
if node.L == None:
node.L = Node(key)
There is also a problem with this line:
self._insert(key, Node(node.L))
The way you're doing it right now, there is no way to access that last reference of Node() because your _insert function inserted it under an anonymously constructed node that has no parent node, and therefore is not a part of your tree. That node being passed in to your insert function is not the L or R of any other node in the tree, so you're not actually adding anything to the tree with this.
Now that we changed the Ls and Rs to be Nodes, you have a way to pass in a node that's part of the tree into the insert function:
self._insert(key, node.L)
Now you're passing the node's left child into the recursive insert, which by the looks of thing is what you were originally trying to do.
Once you make these changes in your code for both the L and R insert cases you can get to the last instance of Node() in your
10
\
11
\
12
example tree via myTree.root.R.R. You can get its key via myTree.root.R.R.key, which equals 12.
Most of you're questions come from not finishing the program; In your current code after myTree.insert(11) you're tree is setting R equal to a int rather than another Node.
If the value isn't found then create the new node at that point. Otherwise pass the next node into the recursive function to keep moving further down the tree.
def _insert(self, key, node):
if key < node.key:
if node.L == None:
node.L = Node(key)
else:
self._insert(key, node.L)
if key > node.key:
if node.R == None:
node.R = Node(key)
else:
self._insert(key, node.R)
P.S. This isn't finished you're going to need another level of logic testing incase something is bigger than the current Node.key but smaller than the next Node.

How to replace a subtree in python

I have my tree data structure as below:
class Node(object):
def __init__(self, data):
self.data = data
self.children = []
def add_child(self, obj):
self.children.append(obj)
Then I created a method to accomplish it.
def replace(node, newNode):
if node.data == 1:
node = newNode
return
else:
for i in xrange(0, len(node.children)):
replace(node.children[i], newNode)
This method is called just like that:
replace(mytree,newNode)
Since it is recursive call, I think the object get destroyed and the assignment does not happen.
I tried it manually as:
mytree.children[0].children[0] = newNode
then the tree is correctly updated. How can I achieve it using my method above?
The assignment node = newNode doesn't do what you want. It doesn't replace the object you know as node with newNode everywhere. It just rebinds the local variable name node to point to the same object as the other local name newNode. Other references to the first node (such as in its parent's children list) will be unchanged.
To actually do what you want requires more subtlety. The best approach is often often not to replace the node at all, but rather to replace its contents. That is, set node.data and node.children to be equal to newNode.data and newNode.children and leave node in place. This only fails to work properly if there are other references to node or newNode and you want them to work properly after the replacement.
The alternative is to do the replacement in the parent of the node you're looking for. This won't work at the top of your tree, so you'll need special logic to handle that situation.
def replace(node, newNode):
if node.value == 1:
raise ValueError("can't replace the current node this way")
for index, child in enumerate(node.children):
if child.data == 1:
node.children[index] = newNode
return True
if replace(child, newNode):
return True
return False
I've also added some extra logic to stop the recursive processing of the tree when the appropriate node has been found. The function will return True if a replacement has been made, or False if the right data value was not found.

Problem building a complete binary tree of height 'h' in Python

Here is my code. The complete binary tree has 2^k nodes at depth k.
class Node:
def __init__(self, data):
# initializes the data members
self.left = None
self.right = None
self.data = data
root = Node(data_root)
def create_complete_tree():
row = [root]
for i in range(h):
newrow = []
for node in row:
left = Node(data1)
right = Node(data2)
node.left = left
node.right = right
newrow.append(left)
newrow.append(right)
row = copy.deepcopy(newrow)
def traverse_tree(node):
if node == None:
return
else:
traverse_tree(node.left)
print node.data
traverse_tree(node.right)
create_complete_tree()
print 'Node traversal'
traverse_tree(root)
The tree traversal only gives the data of root and its children. What am I doing wrong?
The main problem here is that you are using deepcopy on the temporary list. Consider what happens each iteration:
Your initial root gets inspected, and child nodes get created
These child nodes are placed in newrow
Copies of these child nodes are copied into row for the next iteration.
This means the subsequent iteration will not be mutating the nodes you created (and which root.left and root.right points to), but copies of them, leaving the originals in their current state (with None for .left and .right)

Categories

Resources