I am trying to implement binary search tree in pythonand trying to print the nodes of a tree in inorder, preorder and postorder but unfortunately my results are not correct.
Here is my code:
class Node:
def __init__(self, val):
self.v = val
self.l = None
self.r = None
class BinarySearchTree:
def __init__(self):
self.root = None
def get_root(self):
return self.root
def insert(self, val):
if self.root is None:
self.root = Node(val)
else:
self._add(val, self.root)
def _add(self, val, node):
if val < node.l:
if node.l is None:
node.l = Node(val)
else:
self._add(val, node.l)
else:
if node.r is None:
node.r = Node(val)
else:
self._add(val, node.r)
def find(self, val):
if self.root is None:
return None
else:
self._find(val, self.root)
def _find(self, val, node):
if val == node.v:
return Node
else:
if val < node.v and node is not None:
self._find(val, node.l)
if val > node.v and node is not None:
self._find(val, node.r)
def delete_tree(self):
self.root = None
def print_in_order(self): # Left, Node, Right
if self.root is None:
return None
else:
self._in_order(self.root)
def _in_order(self, node):
if node is not None:
self._in_order(node.l)
print str(node.v) + ' '
self._in_order(node.r)
def print_pre_order(self): # Node, Left, Right
if self.root is None:
return None
else:
self._pre_order(self.root)
def _pre_order(self, node):
if node is not None:
print str(node.v) + ' '
self._pre_order(node.l)
self._pre_order(node.r)
def print_post_order(self): # Left, Right, Node
if self.root is None:
return None
else:
self._post_order(self.root)
def _post_order(self, node):
if node is not None:
self._post_order(node.l)
self._post_order(node.r)
print str(node.v) + ' '
if __name__ == '__main__':
t = BinarySearchTree()
t.insert(20)
t.insert(10)
t.insert(30)
t.insert(5)
t.insert(15)
t.insert(25)
t.insert(35)
print 'In Order Traversal: \n', t.print_in_order()
print '\nPre Order Traversal: \n', t.print_pre_order()
print '\nPost Order Traversal:\n', t.print_post_order()
Can someone please tell me what am I doing wrong?
My output is in the following: Inorder and Preorder is returning the same output.
In Order Traversal:
20
10
30
5
15
25
35
None
Pre Order Traversal:
20
10
30
5
15
25
35
None
Post Order Traversal:
35
25
15
5
30
10
20
None
Traversal functions are okay. But in _add, the following comparison:
if val < node.l:
...
should be replaced with:
if val < node.v:
...
to compare new value with current node value, instead of the left node which cause wrong comparison result; results in wrong tree structure.
Related
I got the problem to find the height from a partially completed code sample of BST implementation as below.
class BST:
class Node:
def __init__(self, key, left=None, right=None):
self.key = key
self.left = left
self.right = right
def __iter__(self):
if self.left:
yield from self.left
yield self.key
if self.right:
yield from self.right
def __init__(self, root=None):
self.root = root
def __iter__(self):
if self.root:
yield from self.root
def insert(self, key):
self.root = self._insert(self.root, key)
def _insert(self, r, key):
if r is None:
return self.Node(key)
elif key < r.key:
r.left = self._insert(r.left, key)
elif key > r.key:
r.right = self._insert(r.right, key)
else:
pass
return r
def print(self):
self._print(self.root)
def _print(self, r):
if r:
self._print(r.left)
print(r.key, end=' ')
self._print(r.right)
def contains(self, k):
n = self.root
while n and n.key != k:
if k < n.key:
n = n.left
else:
n = n.right
return n is not None
def size(self):
return self._size(self.root)
def _size(self, r):
if r is None:
return 0
else:
return 1 + self._size(r.left) + self._size(r.right)
I tried to implement "def height(self): " method, referencingBinary search tree
I tried left and right nodes as below
for node in self.root.__iter__():
print(node)
But, I was unable to understand how to work with generators and implement this height method.Hope your support. Thanks in advance.
This is my BST code:
class BinarySearchTree(BinaryTree):
def insert(self, value):
parent = None
x = self.root
while(x):
parent = x
if value < x.data:
x = x.left
else:
x = x.right
if parent is None:
self.root = Node(value)
elif value < parent.data:
parent.left = Node(value)
else:
parent.right = Node(value)
def search(self, value):
return self._search(value, self.root)
def _search(self, value, node):
if node is None:
return node
if node.data == value:
return BinarySearchTree(node)
if value < node.data:
return self._search(value, node.left)
return self._search(value, node.right)
and i want it to open a .txt file that have words like this
apple
grape
banana
lemon
and only show the first 4 levels
I have problem with infinite loop in getMinimal() method. It works in this way :
1)Take node,
2)If node has other node on the left - go to other one.
3)Repeat as far as node has sth on the left side
4)Return the minimal node.
But sometimes it works in infinite loop for example from 1000 to 400, then to 4 then..to 1000! I have no ide where I make mistake. I reviewed this code many times,every single "pointer" to parent/left/right node is okay! Please - help.
Algorithm works okay to "handwritten" trees - ~20nodes. I wanted to test it in better cases - 2500nodes,generated by random lib (from -10k to 10k).
import random
class Node:
def __init__(self, val):
self.val = val
self.parent = None
self.right = None
self.left = None
# Class of node.
def str(self):
return str(self.val)
class MyTree:
def __init__(self, node):
self.root = node
def insert(self, node):
current = self.root
a = True
while a:
if node.val > current.val:
if current.right is not None:
current = current.right
continue
else:
current.right = node
node.parent = current
a = False
if node.val <= current.val:
if current.left is not None:
current = current.left
continue
else:
current.left = node
node.parent = current
a = False
def search(self, node):
current = self.root
while node.val != current.val:
if node.val > current.val:
current = current.right
continue
elif node.val <= current.val:
current = current.left
continue
if node.val == current.val:
return current
else:
print("There is no such node!")
def delete(self, node):
if isinstance(node, (float, int)):
node = self.search(node)
if node is self.root:
self.__deleteRoot()
return
else:
if node.right is None and node.left is None:
self.__deleteNN(node)
return
if node.right is None and node.left is not None:
self.__deleteLN(node)
return
if node.right is not None and node.left is None:
self.__deleteNR(node)
return
if node.right is not None and node.left is not None:
self.__deleteLR(node)
return
def __deleteNN(self, node):
if node.parent.left is node:
node.parent.left = None
if node.parent.right is node:
node.parent.right = None
def __deleteLN(self, node):
parent = node.parent
son = node.left
# parent replaced
if parent.left is node:
parent.left = son
if parent.right is node:
parent.right = son
son.parent = parent
def __deleteNR(self,node):
parent = node.parent
son = node.right
# replace parent
if parent.left is node:
parent.left = son
if parent.right is node:
parent.right = son
son.parent = parent
def __deleteLR(self, node):
minimal = self.getMinimal(node.right)
if minimal.parent.left is minimal:
minimal.parent.left = None
if minimal.parent.right is minimal:
minimal.parent.right = None
# parent of minimal done..
if node.parent.left is node:
node.parent.left = minimal
if node.parent.right is node:
node.parent.right = minimal
minimal.right = node.right
minimal.left = node.left
def getMinimal(self, node):
k = node
while k.left is not None:
k = k.left
return k
def getMax(self):
current = self.root
while current.right:
current = current.right
return current
def __trav(self, node):
if not node:
return
print(node.val)
self.__trav(node.left)
self.__trav(node.right)
def printTrav(self):
self.__trav(self.root)
def __deleteRoot(self):
if self.root.left is None and self.root.right is None:
self.root = None
return
if self.root.left is None and self.root.right is not None:
# left empty,right full
self.root.right.parent = None
self.root = self.root.right
return
if self.root.left is not None and self.root.right is None:
# right empty, left full
self.root.left.parent = None
self.root = self.root.left
return
# node has both children
if self.root.left is not None and self.root.right is not None:
temp = self.getMinimal(self.root.right) # minimal from right subtree
# sometimes it could be like this..
# r
# \
# x
if temp.parent.left is temp:
temp.parent.left = None
else:
temp.parent.right = None
self.root.left.parent = temp
self.root.right.parent = temp
temp.right = self.root.right
temp.left = self.root.left
self.root = temp
self.root.parent = None
return
def search(self, val):
node = self.root
if node.val == val:
return node
if val > node.val and node.right is not None:
node = node.right
if val < node.val and node.left is not None:
node = node.left
else:
print("There's no such value!")
return
def printMax(self):
print(self.getMax().val)
def printMin(self):
print(self.getMinimal(self.root).val)
arr=[None]*2500
for x in range(2500):
arr[x]=Node(random.randint(-10000,10000))
myTree = MyTree(arr[0])
for x in range(1,2500):
myTree.insert(arr[x])
for x in range(2500):
myTree.delete(arr[x])
It is suspicious that you define search twice.
Still that said, here is how I would debug this. I would modify your program to read from a file, try to run, and then detect an endless loop and bail out. Now write random files until you have one that causes you to crash.
Once you have a random file that shows the bug, the next step is to make it minimal. Here is a harness that can let you do that.
import itertools
flatten = itertools.chain.from_iterable
# bug_found should be a function that takes a list of elements and runs your test.
# example should be an array that demonstrates the bug.
def find_minimal (bug_found, example):
parts = [example]
while 1 < max(len(part) for part in parts):
i = 0
while i < len(parts):
if 1 == len(parts[i]):
i = i + 1
else:
part = parts.pop(i)
# Divide in 2.
mid = len(part)/2
part1 = part[0:mid]
part2 = part[mid:len(part)]
# Do we need part1?
parts.insert(i, part1)
if bug_found(flatten(parts)):
i = i + 1
parts.insert(i, part2)
else:
parts[i] = part2
# Do we need part2?
if bug_found_func(flatten(parts)):
i = i + 1
else:
parts.pop(i)
return list(flatten(parts))
Just let it run, and after some time it is likely to find a small example. Which will greatly aid in debugging.
So - I found 2 serious bugs in code. Both in LR ("standard" node and root). As I suspected - bugs were in pointers. Now tree is working (tested few times for 20k,30k and 100k nodes). Solved.
I'm reading the following data as part of an assignment into a binary tree (not a strict binary search tree):
5
4 1 2
2 3 4
5 -1 -1
1 -1 -1
3 -1 -1
They're being read into three lists in python self.key, self.left and self.right where the first line has the integer n is the number of nodes. The next n lines are key, left, right. Where left is the key of the left child of the parent is key[left] and likewise the key of the right child is key[right], so for example the first line is the key of 4 is the root and key[1] meaning 2 is the left child of 4 and key[2] meaning 5 is the right child of 4 and so on and -1 for left and right means this key is a leaf:
Tree structure for this example
The problem is the left and right children of the root are being added but none of the children of these are being added. Am I correctly adding nodes to the tree? I cannot just add them based on value of the key because it's not a strict binary search tree as some other examples make clear, such as root = 0 and left child = 70 and right child = 20. The output of inOrder traversal is 2 4 5 (should be 1 2 3 4 5) which leads me to believe I'm not adding the further nodes. Any help on the adding methods would be appreciated...
import sys, threading
sys.setrecursionlimit(10**6) # max depth of recursion
threading.stack_size(2**27) # new thread will get stack of such size
class Node:
def __init__(self, val):
self.l = None
self.r = None
self.v = val
class Tree:
def __init__(self):
self.root = None
def getRoot(self):
return self.root
def add_root(self, val):
if(self.root is None):
self.root = Node(val)
def add_left(self, val, node):
if(node.l is None):
node.l = Node(val)
def add_right(self, val, node):
if(node.r is None):
node.r = Node(val)
def deleteTree(self):
# garbage collector will do this for us.
self.root = None
def inOrder(self):
self.result = []
if(self.root is not None):
self._inOrder(self.root, self.result)
return self.result
else:
print('root is None')
def _inOrder(self, node, result):
if(node != None):
self._inOrder(node.l, self.result)
self.result.append(node.v)
self._inOrder(node.r, self.result)
def read(self):
self.n = int(sys.stdin.readline())
self.key = [0 for i in range(self.n)]
self.left = [0 for i in range(self.n)]
self.right = [0 for i in range(self.n)]
for i in range(self.n):
[a, b, c] = map(int, sys.stdin.readline().split())
self.key[i] = a
self.left[i] = b
self.right[i] = c
#adding root
self.add_root(self.key[0])
if self.left[0] != -1:
#add left of root
self.add_left(self.key[self.left[0]], self.root)
if self.right[0] != -1:
#add right of root
self.add_right(self.key[self.right[0]], self.root)
#where it is not adding left and right nodes
for i in range(1, self.n):
if self.left[i] != -1:
# adding the other left nodes
self.add_left(self.key[self.left[i]], Node(self.key[i]))
if self.right[i] != -1:
# adding the other right nodes
self.add_right(self.key[self.right[i]], Node(self.key[i]))
def main():
tree = Tree()
tree.read()
print(" ".join(str(x) for x in tree.inOrder()))
#print(" ".join(str(x) for x in tree.preOrder()))
#print(" ".join(str(x) for x in tree.postOrder()))
threading.Thread(target=main).start()
Thanks I got it to work - I added the nodes Node(key[i]) to a dictionary and self.nodes[val] = [node, node.l, node.r] and when adding the left and recursively searched the dictionary for inOrder, preOrder and postOrder tree traversals.
class Node:
def __init__(self, val):
self.l = None
self.r = None
self.v = val
class Tree:
def __init__(self):
self.root = None
self.nodes = {}
def getRoot(self):
return self.root
def add_root(self, val):
if(self.root is None):
self.root = Node(val)
self.nodes[val] = [self.root,-1,-1]
def add_left(self, val, node):
if(node.l is None):
node.l = Node(val)
self.nodes[node.v][1] = node.l
def add_right(self, val, node):
if(node.r is None):
node.r = Node(val)
self.nodes[node.v][2] = node.r
def inOrder(self):
self.result = []
if(self.root is not None):
self._inOrder(self.root, self.result)
return self.result
else:
print('root is None')
def _inOrder(self, node, result):
if(node is not None):
try:
self._inOrder(self.nodes[node.v][1], self.result)
except (IndexError, AttributeError):
pass
self.result.append(node.v)
try:
self._inOrder(self.nodes[node.v][2], self.result)
except (IndexError, AttributeError):
pass
def preOrder(self):
self.result = []
if(self.root is not None):
self._preOrder(self.root, self.result)
return self.result
else:
print('root is None')
def _preOrder(self, node, result):
if(node is not None):
self.result.append(node.v)
try:
self._preOrder(self.nodes[node.v][1], self.result)
except (IndexError, AttributeError):
pass
try:
self._preOrder(self.nodes[node.v][2], self.result)
except (IndexError, AttributeError):
pass
def postOrder(self):
self.result = []
if(self.root is not None):
self._postOrder(self.root, self.result)
return self.result
else:
print('root is None')
def _postOrder(self, node, result):
if(node is not None):
try:
self._postOrder(self.nodes[node.v][1], self.result)
except (IndexError, AttributeError):
pass
try:
self._postOrder(self.nodes[node.v][2], self.result)
except (IndexError, AttributeError):
pass
self.result.append(node.v)
def read(self):
self.n = int(sys.stdin.readline())
self.key = [0 for i in range(self.n)]
self.left = [0 for i in range(self.n)]
self.right = [0 for i in range(self.n)]
for i in range(self.n):
[a, b, c] = map(int, sys.stdin.readline().split())
self.key[i] = a
self.left[i] = b
self.right[i] = c
#adding root
self.add_root(self.key[0])
for i in range(1, self.n):
self.nodes[self.key[i]] = [Node(self.key[i]),-1,-1]
for i in range(0, self.n):
if self.left[i] != -1:
# adding the other left nodes
self.add_left(self.key[self.left[i]], self.nodes[self.key[i]][0])
if self.right[i] != -1:
# adding the other right nodes
self.add_right(self.key[self.right[i]], self.nodes[self.key[i]][0])
I am trying to practice BST tree implementation with python, following is my code,
import pdb
class Node():
def __init__(self, parent=None, key=None):
self.parent = parent if parent != None else None
self.left = None
self.right = None
self.key = key if key != None else None
class BST():
def __init__(self):
self.root = Node()
def insertKey (self, key):
#pdb.set_trace()
# transverse till we find empty position
if (self.root.key == None):
self.root.key = key
else:
node = self.root
while (node.left != None and node.right != None):
if node.key < key:
node = node.right
else:
node = node.left
#we have node either left or right is empty
if node.key < key:
node.right = Node (node, key)
else:
node.left = Node (node, key)
def inOrder (self, node):
#pdb.set_trace()
if node != None:
self.inOrder (node.left)
print node.key
self.inOrder (node.right)
def printLeft (self, node):
if node != None:
self.printLeft (node)
print node.key
def debugAll (self):
self.inOrder (self.root)
#self.printLeft (self.root)
def fromArray (self, numbers):
srt = sorted(numbers)
print srt
length = len(srt)
mid = length/2
rootEle = srt[mid]
self.insertKey (rootEle)
for i in range (1, mid+1):
try:
#pdb.set_trace()
self.insertKey (srt[mid-i])
self.insertKey (srt[mid+i])
except IndexError:
pass
bst = BST()
bst.fromArray ([1,2,4,3,6,5,10,8,9])
bst.debugAll ()
However the result of the inOrder tree walk is unexpected
1
4
5
6
10
I tried to debug through the pdb while inserting the keys, the keys are properly inserted, but when transversing the tree, some Node are skipped because they're marked as 'NoneType'. May be I am missing out on some language specifics here.
For a start, the code you have below isn't right:
while (node.left != None and node.right != None):
if node.key < key:
node = node.right
else:
node = node.left
It will stop descending if either the left or the right node doesn't exist.
EDIT: If you modify the loop like this, it works. Could be better optimized, but it's a start...
class Node():
def __init__(self, parent=None, key=None):
self.parent = parent if parent != None else None
self.left = None
self.right = None
self.key = key if key != None else None
class BST():
def __init__(self):
self.root = Node()
def insertKey (self, key):
#pdb.set_trace()
# transverse till we find empty position
if (self.root.key == None):
self.root.key = key
else:
node = self.root
while 1:
if node.key < key:
if node.right is None:
node.right = Node(node, key)
break
else:
node = node.right
else:
if node.left is None:
node.left = Node(node, key)
break
else:
node = node.left
def inOrder (self, node):
#pdb.set_trace()
if node != None:
self.inOrder (node.left)
print node.key
self.inOrder (node.right)
def printLeft (self, node):
if node != None:
self.printLeft (node)
print node.key
def debugAll (self):
self.inOrder (self.root)
#self.printLeft (self.root)
def fromArray (self, numbers):
srt = sorted(numbers)
print srt
length = len(srt)
mid = length/2
rootEle = srt[mid]
self.insertKey (rootEle)
for i in range (1, mid+1):
try:
#pdb.set_trace()
self.insertKey (srt[mid-i])
self.insertKey (srt[mid+i])
except IndexError:
pass
bst = BST()
bst.fromArray ([1,2,4,3,6,5,10,8,9])
bst.debugAll ()