delete node in BST python - python-3.x

class Node:
def __init__(self,data):
self.data = data
self.left = None
self.right = None
self.count = 1
class BST:
def __init__(self):
self.root = None
self.size = 0
def _insert(self,val,node):
if val<node.data:
if node.left:
self._insert(val,node.left)
else:
self.size+=1
node.left = Node(val)
elif val>node.data:
if node.right:
self._insert(val,node.right)
else:
self.size+=1
node.right = Node(val)
else:
self.size+=1
node.count+=1
def insert(self,val):
if not self.root:
self.size+=1
self.root = Node(val)
else:
self._insert(val,self.root)
def _inorder(self,node):
if node:
self._inorder(node.left)
self.bstview.append(node.data)
self._inorder(node.right)
def view(self):
if self.root:
self.bstview = []
self._inorder(self.root)
return self.bstview
def _min(self,node):
if node.left:
return self._min(node.left)
return node
def _max(self,node):
if node.right:
return self._max(node.right)
return node
def min(self):
if self.root:
return self._min(self.root).data
def max(self):
if self.root:
return self._max(self.root).data
def _remove(self,val,node):
if not node:
return
if val < node.data:
node.left = self._remove(val,node.left)
elif val > node.data:
node.right = self._remove(val,node.right)
else:
if node.count >1:
self.size-=1
node.count-=1
return node
elif not node.left:
temp = node.right
del node
self.size-=1
return temp
elif not node.right:
temp = node.left
del node
self.size-=1
return temp
else:
temp = self._min(node.right)
node.data = temp.data
node.right = self._remove(temp.data,node.right)
return node #WHY THIS IS NECESSARY PLEASE EXPLAIN!!
def remove(self,val):
if self.root:
self._remove(val,self.root)
b = BST()
for x in list(map(int,"20 10 30 5 15 25 35 3 7 23 27 40 4 45".split(" "))):
b.insert(x)
print(b.view())
print(b.min(),b.max(),b.size)
b.remove(20)
b.remove(45)
b.remove(1)
print(b.view())
print(b.min(),b.max(),b.size)
In the above code of BST, why is it necessary to return node in the _remove() method? The if/elif statements are already returning nodes?
Also, I would be greatly thankful to you if you can guide me to an easy/less time consuming way to understand recursive functions (I know what they are and how they work, my question is if someone asks me how a particular recursive function works then I would be able to explain him without confusing myself).

Related

Python pre-order to postfix notation reverse polish notation

I am trying to find a solution in Python to try and move the pre-order notation e.g."* + 7 3 - 2 9" or "+ 55 26" to post notation or the reverse polish notation. the expected result would be e.g. "7 3 + 2 9 - *" and "55 26 +" respectively. I did some research on the binary trees however with the math functions I am struggling. What I have currently is the following:
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.data = data
Function to check for operators
def isOperator(self, c):
return c == '+' or c == '-' or c == '*' or c == '/'
Insert Node
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
Print the Tree
def PrintTree(self):
if self.left:
self.left.PrintTree()
print( self.data),
if self.right:
self.right.PrintTree()
Preorder traversal
Root -> Left ->Right
def PreorderTraversal(self, root):
res = []
if root:
res.append(root.data)
res = res + self.PreorderTraversal(root.left)
res = res + self.PreorderTraversal(root.right)
return res
Postorder traversal
Left ->Right -> Root
def PostorderTraversal(self, root):
res = []
if root:
res = self.PostorderTraversal(root.left)
res = res + self.PostorderTraversal(root.right)
res.append(root.data)
return res
Any assistance in getting this code complete will be highly appreciated, as I am not able to figure this out.
Thank you in advance
First some issues:
Your insert method applies binary search tree logic, which is not relevant for the expression tree you want to build. Instead it should keep track of the insertion spot for any next data.
It is not clear whether these are all methods of the Node class, but at least isOperator is unrelated to any instance values, so it should not be a method (with self argument), but either a stand alone function or a static method. Moreover, if the input is valid, then an operator is anything that is not a number.
Methods should not print (except for debugging purposes): leave that for the main driver code to do. Methods can help printing by returning an iterator, or a representation (implementing __repr__).
It is common practice to not use an initial capital letter for function names and reserve that for class names.
I would suggest a separate class for the tree. This will be handy to track where the next node should be inserted, using a path (stack).
Here is how you could implement it:
class Node:
def __init__(self, data, left=None, right=None):
self.left = left
self.right = right
self.data = data
def __iter__(self): # default iteration is inorder
if self.left:
yield from self.left
yield self.data
if self.right:
yield from self.right
def preorder(self):
yield self.data
if self.left:
yield from self.left.preorder()
if self.right:
yield from self.right.preorder()
def postorder(self):
if self.left:
yield from self.left.postorder()
if self.right:
yield from self.right.postorder()
yield self.data
class Tree:
def __init__(self):
self.root = None
self.path = []
def insert(self, data):
if not self.root:
node = self.root = Node(data)
elif not self.path:
raise ValueError("Cannot add more nodes")
elif self.path[-1].left:
node = self.path[-1].right = Node(data)
else:
node = self.path[-1].left = Node(data)
if not data.isnumeric(): # internal node
self.path.append(node)
else:
while self.path and self.path[-1].right:
self.path.pop()
#staticmethod
def fromstr(s):
tree = Tree()
for token in s.split():
tree.insert(token)
return tree
def __iter__(self):
if self.root:
yield from self.root
def preorder(self):
if self.root:
yield from self.root.preorder()
def postorder(self):
if self.root:
yield from self.root.postorder()
Here is how you can run it:
def postorder(s):
tree = Tree.fromstr(s)
# Just for debugging, print the tree in inorder:
print(*tree) # this calls `__iter__`
return " ".join(tree.postorder())
print(postorder("* + 7 3 - 2 9"))
Output (inorder and postorder):
7 + 3 * 2 - 9
7 3 + 2 9 - *

search a node in a binary tree

i have this code that suppose seek the node in a binary tree:
def seekNode(self, node):
if self.id == node:
return self
elif self.left != None:
self.left.seekNode(node)
elif self.right != None:
self.left.seekNode(node)
else:
return False
but it returns a None object.
Here is the complete class:
class BinTree:
def __init__(self, id=None):
self.id= id
self.left= None
self.right= None
def setId(self, id):
if self.id == None:
self.id= id
return self
else:
return self.seekNode(id)
def addChildLeft(self, left):
if self.left == None:
self.left= BinTree(left)
else:
self.left.addChildLeft(left)
def addChildRight(self, right):
if self.right == None:
self.right= BinTree(right)
else:
self.right.addChildRight(right)
def seekNode(self, node):
if self.id == node:
return self
elif self.left != None:
self.left.seekNode(node)
elif self.right != None:
self.left.seekNode(node)
else:
return False
i only call setuId function, i debugged the code (i'm not expert doing this) and i saw that the function returned de object, but when i save this search, it turns in None object.
You are missing return before the recursive calls to self.left.seek(...) and self.right.seek(...). The user of the tree also should not have to concern him/herself with addLeft or addRight - a simple insert method should automatically insert a new node in the proper location.
I would also point out recursion is a functional heritage and using it with functional style will yield the best results. This means avoiding things variable reassignment, other mutations, and side effects.
Let's see what it would look like to implement btree in this way -
# btree.py
def empty():
return None
def insert(t, v):
if not t:
return node(v) # empty
elif v < t.data:
return node(t.data, insert(t.left, v), t.right) # insert left
elif v > t.data:
return node(t.data, t.left, insert(t.right, v)) # insert right
else:
return t # no change
def seek(t, v):
if not t:
return None # no match
elif v < t.data:
return seek(t.left, v) # seek left
elif v > t.data:
return seek(t.right, v) # seek right
else:
return t # match
The data structure for node can be anything you want. Here's a dead-simple interface. Notice how the left and right properties can be set when a new node is constructed -
class node:
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right

Why is hashing failing in LRU Cacheing Problem in Python

I recently stumbled across an example online for solving the LRUCache problem for interview questions. When I copied the end code into an IDE and compile/run the code, it was giving a KeyError while running through the test code in the main section.
class DLL:
def __init__(self, key, value):
self.key = key
self.value = value
self.next = None
self.prev = None
class LRUCache:
def __init__(self, capacity: int):
self.head = DLL(-1, -1)
self.tail = self.head
self.tracker = {}
self.capacity = capacity
self.length = 0
def get(self, key: int) -> int:
if key not in self.tracker:
return -1
node = self.tracker[key]
value = node.value
while node.next:
node.prev.next = node.next
node.next.prev = node.prev
self.tail.next = node
node.prev = self.tail
node.next = None
self.tail = node
return value
def put(self, key: int, value: int) -> None:
if key not in self.tracker:
node = self.tracker[key]
node.value = value
while node.next:
node.prev.next = node.next
node.next.prev = node.prev
self.tail.next = node
node.prev = self.tail
node.next = None
self.tail = node
else:
node = DLL(key, value)
self.tracker[hash] = node
self.tail.next = node
node.prev = self.tail
self.tail = node
self.length += 1
if self.length > self.capacity:
remove = self.head.next
self.head.next = self.head.next.next
self.head.next.prev = self.head
del self.tracker[remove.key]
self.length += 1
if __name__ == '__main__':
lru = LRUCache(3)
lru.put(1, 2)
lru.put(2, 2)
lru.put(3, 2)
lru.get(3)
lru.get(1)
lru.put(4, 2)
lru.get(4)
With an error message spitting out:
Traceback (most recent call last):
File "LRUCache.py", line 63, in <module>
lru.put(1, 2)
File "LRUCache.py", line 36, in put
node = self.tracker[key]
KeyError: 1
The problem is linked to the line self.tracker[hash] = node but I cannot figure out why. Can someone please explain what is going on here? The original posting does not have a comments section, so I have been stuck trying to figure out someone else's code on my own.

Python, Linked list: AttributeError: 'function' object has no attribute 'get_next'

I'm trying to do an implementation of a simple linked list but I keep getting this exception: AttributeError: 'function' object has no attribute 'get_next'.
here is my code:
class Lista:
def __init__(self):
self.root = None
self.len_l = 0
def __str__(self):
if not self.len_l:
return "::EMPTY::"
lista_c = ""
next_node = self.root
while next_node:
if next_node.get_next():
if type(next_node.get_datos()) != str:
lista_c += "%s -> "%(next_node.get_datos())
else:
lista_c += "'%s' -> "%(next_node.get_datos())
else:
if type(next_node.get_datos()) != str:
lista_c += " %s"%(next_node.get_datos())
else:
lista_c += " '%s'"%(next_node.get_datos())
next_node = next_node.get_next()
return lista_c
def add(self, dato):
self.root = nodo(dato,self.root)
self.len_l += 1
def find(self,dato):
if not self.len_l:
raise LSLexception("empty list")
this_node = self.root
while this_node.get_next():
if dato == this_node.get_datos():
return this_node
this_node = this_node.get_next()
return -1
def Valid_pos(self,pos):
if self.len_l == 0:
raise LSLexception("empty list")
if pos not in range(0,self.len_l):
raise LSLexception("data overflow")
def remove(self,dato):
if not self.len_l:
raise LSLexception("empty list")
if self.root.get_datos() == dato:
self.root = self.root.get_next()
previous_node = self.root
this_node = previous_node.get_next()
while this_node.get_next():
if this_node.get_datos() == dato:
previous_node.set_next(this_node.get_next())
return this_node
else:
previous_node = this_node
this_node = previous_node.get_next()
return -1
The problem occurs in the function remove in this line while this_node.get_next(): but I'm using that same line in the function "find" an I. Works fine, any ideas? Is for college so there are some things I can't change.
You forget to put parentheses when you try to call get_next, so some of the node variables are assigned to a function instead of a node.
Like this_node = previous_node.get_next in def remove(self, dato), you might want to change that to
this_node = previous_node.get_next()
Also, set_next in previous_node.set_next = this_node.get_next also looks like a function to me. You might want to change that too.

Function to Calculate the height of a Binary Search Tree is not working properly

I have tried various methods in order to calculate the height of a Binary Search Tree which includes recursion and also using a list in order to add the node along with it's depth.But for both the methods,the output is not correct.
Here's my code for the same:
class Node:
def __init__(self,data):
self.data=data
self.left=None
self.right=None
def Insert_BTreeNode(self,data):
if self.data:
if data<=self.data:
if self.left is None:
self.left=Node(data)
else:
self.left.Insert_BTreeNode(data)
elif data>self.data:
if self.right is None:
self.right=Node(data)
else:
self.right.Insert_BTreeNode(data)
else:
self.data=data
def Lookup(self,data,parent=None):
if data<self.data:
if self.left is None:
return None,None
return self.left.Lookup(data,self)
elif data>self.data:
if self.right is None:
return None,None
return self.right.Lookup(data,self)
else:
if (parent is not None):
print(self.data,parent.data)
return (self,parent)
def Children_count(self):
count=0
if self.left:
count+=1
if self.right:
count+=1
return (count)
def Delete(self,data):
children_count=0
node,parent=self.Lookup(data)
if node is not None:
children_count=node.Children_count()
if children_count==0:
if parent:
if parent.left is Node:
parent.left=None
else:
parent.right=None
del node
else:
self.data=data
elif children_count==1:
if node.left:
n=node.left
else:
n=node.right
if parent:
if parent.left is node:
parent.left=n
else:
parent.right=n
del node
else:
self.left=n.left
self.right=n.right
self.data=n.data
else:
parent=node
successor=node.right
while successor.left:
parent=successor
successor=successor.left
node.data=successor.data
if parent.left==successor:
parent.left=successor.right
else:
parent.right=successor.right
def print_treeInorder(self):
if self.left:
self.left.print_treeInorder()
print(self.data)
if self.right:
self.right.print_treeInorder()
def print_treePostorder(self):
if self.left:
self.left.print_treePostorder()
if self.right:
self.right.print_treePostorder()
print(self.data)
def height(self):
if self is None:
return 0
else:
return max(height(self.getLeft()), height(self.getRight()))+ 1
def print_treePreorder(self):
print(self.data)
if self.left:
self.left.print_treePreorder()
if self.right:
self.right.print_treePreorder()
def getLeft(self):
return self.left
def getRight(self):
return self.right
def maxDepth(self): #Level order Traversal
if self is None:
return 1
q=[]
q.append([self,1])
while(len(q))!=0:
node,temp=q.pop()
if node.getLeft()!=None:
q.append([node.getLeft(),temp+1])
if node.getRight()!=None:
q.append([node.getRight(),temp+1])
return temp
b_tree_input=list(map(int,input().split()))
root=Node(b_tree_input[0])
for i in range(1,len(b_tree_input)):
root.Insert_BTreeNode(b_tree_input[i])
print(root.height())
print(root.maxDepth())
For the sample input of 2,1,3,4.Both the function should return the answer as 3 but the height function returns the following.
NameError: name 'height' is not defined
While the maxDepth() function returns the answer as 2.
Regarding height: I'm not a python programmer, but shouldn't it be
max(self.getLeft().height(), self.getRight().height())
// instead of
max(height(self.getLeft()), height(self.getRight()))
since height is a member function?
However, this implies you have to check self.getLeft() and self.getRight() for None values before calling height. If you don't want to do the extra checks, think of making height a global function unrelated to your class, then your previous approach on recursion should work.
The height function with checks for None could look as follows (no guarantee on the syntax details):
def height(self):
if self is None:
return 0
elif self.left and self.right:
return max(self.left.height(), self.right.height()) + 1
elif self.left:
return self.left.height() + 1
elif self.right:
return self.right.height() + 1
else:
return 1
In maxDepth you process child nodes in a right-to-left DFS but you only take the result of the last processed path instead of comparing current temp to the maximum of already found depths.
So in your [2,1,3,4] example, the execution order is as follows:
q=[[2,1]] -> take [2,1] -> temp=1
q=[[1,2],[3,2]] -> take [3,2] -> temp=2
q=[[1,2],[4,3]] -> take [4,3] -> temp=3
q=[[1,2]] -> take [1,2] -> temp=2
end
I think you can now figure out how to change your algorithm.
Also you should consider to change the self is None case to return 0.

Resources