I'm trying to build a binary search tree library and I'm getting a syntax error on my piece of code:
class Node:
"""
Tree node: Left and Right child + data which can be any object
"""
def __init__(self,data):
"""
Node constructor
"""
self.left = None
self.right = None
self.data = data
def insert(self,data): # self --> object self.data
"""
Insert new node with data
"""
if data < self.data: # insert data less than current root
if self.left is None: # if left node is empty,
self.left = Node(data) # object left --> Node with new data
else:
self.left.insert(data) # if left node is not empty, go insert again
else:
if self.right is None: # insert data greater than current node
self.right = Node(data) # object right --> Node with new data
else:
self.right.insert(data) # if not empty, run insert again
def lookup(self, data, parent = None):
"""
Lookup node containing data
"""
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:
return self, parent
def delete(self, data):
"""
delete node containing data
"""
""" no child """
if children_count == 0:
# if node has no children, remove it
if parent.left is node: # if parent is pointing to current node
parent.left = None # set parent pointing left to None
else:
parent.right = None # else set parent pointing right to None
del node # del node
""" 1 child """
elif children_count == 1:
# if node has 1 child
# replace node by it's child
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
""" 2 children """
else:
# if node has 2 children
# find its successor
parent = node # parent is equal to current node target of deletion
successor = node.right # successor is right of the node
while successor.left:
parent = successor
successor = successor.left
# replace node data by its successor data
node.data = successor.data
#fix successor's parent's child
if parent.left == successor:
parent.left = successor.right
else:
parent.right = successor.right
def children_count(self):
""" Return the number of children """
if node is None:
return None
cnt = 0
if self.left:
cnt += 1
if self.right:
cnt += 1
return cnt
# method to print tree in order. Use recursion inside print_tree() to walk the tree breath-first
def print_tree(self):
if self.left:
self.left.print_tree()
print self.data,
if self.right:
self.right.print_tree()
the error is:
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
import BSTlib
File "BSTlib.py", line 78
elif children_count == 1:
^
I can't seem to see what is wrong =[ Can someone help me point this out? Thank you!
This is an error with the docstring and whitespace. Run this code:
if False:
print 'hi'
"""1 child"""
elif True:
print 'sdf'
And you get a similar SyntaxError.
Remove the docstring (or just move it) and remove the extra whitespace:
if False:
print 'hi'
elif True:
print 'sdf'
If you need to comment, then just use the hash #:
if False:
print 'hi'
# 1 child
elif True:
print 'sdf'
Related
By making a node to be deleted from the tree, the node (case 1) can be a node with a single arm (right or left), or a node with both branches. In case the node to be deleted is an intermediate node with two branches, there are 2 different methods.
Method 1: the largest knot on the left arm or the smallest knot on the right arm, and
Method 2: The node in the branch with more depth (or the number of elements) is fulfilled so that the right or left arm is balanced.
Both methods have to be coded with separate functions.
How can I do these two methods?
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.parent = None # new
self.data = data
def insert(self, data):
if self.data: # add by comparison
if data < self.data: # left if small
if self.left is None: # add left if left is blank
self.left = Node(data)
self.left.parent = self # new
else:
self.left.insert(data) # if left is not empty add to left sub-tree
elif data > self.data: # right if greater
if self.right is None: # add right if right is blank
self.right = Node(data)
self.right.parent = self # new
else: # if right is not empty add to sub-tree right
self.right.insert(data)
else:
self.data = data # the first dream of the tree
# print Tree
def PrintTree(self):
print( self.data,end='-')
if self.left:
self.left.PrintTree()
if self.right:
self.right.PrintTree()
def sizeTree(self):
if self.left and self.right:
return 1 + self.left.sizeTree() + self.right.sizeTree()
elif self.left:
return 1 + self.left.sizeTree()
elif self.right:
return 1 + self.right.sizeTree()
else:
return 1
def depth(self):
if self.left and self.right:
l = self.left.depth()
r = self.right.depth()
return 1 + max(l,r)
elif self.left:
return 1 + self.left.depth()
elif self.right:
return 1 + self.right.depth()
else:
return 1
# Use the insert method to add nodes
root = Node(25)
root.insert(12)
root.insert(10)
root.insert(22)
root.insert(5)
root.insert(36)
root.insert(30)
root.insert(40)
root.insert(28)
root.insert(38)
root.insert(48)
root.PrintTree()
"""
# 25,36,20,10,5,22,40,48,38,30,22,12,28
root = Node(25)
root.insert(36)
root.insert(20)
root.insert(10)
root.insert(5)
root.insert(22)
root.insert(40)
root.insert(48)
root.insert(38)
root.insert(30)
root.insert(12)
root.insert(28)
print("\n",root.sizeTree(),root.depth())
"""
Some time ago I was playing with this and came up with this code:
def search(self, value):
"""
Recursively looks to the left and right of Tree depending on the provided value
and returns it if it is present within Tree.
Args:
value (int): value to be searched for within Tree
Returns:
value if value exists in Tree otherwise None
"""
if value < self.data:
if self.left is None:
return None
return self.left.search(value)
elif value > self.data:
if self.right is None:
return None
return self.right.search(value)
else:
return self.data
def _findNodeToDelete(self, value, previous=None):
"""
Recursively looks to the left and right of Tree depending on the provided value
and returns it if it is present within Tree.
Args:
value (int): value to be searched for within Tree
Returns:
value if value exists in Tree otherwise None
"""
if value < self.data:
if self.left is None:
return None
return self.left._findNodeToDelete(value, self)
elif value > self.data:
if self.right is None:
return None
return self.right._findNodeToDelete(value, self)
else:
return self, previous
def _mergeNodes(self, target):
self.data = target.data
self.left = target.left
self.right = target.right
def deleteNode(self, value, start=None):
if self.search(value):
to_delete, parent = self._findNodeToDelete(value, start)
if to_delete.right and to_delete.left:
new_value = to_delete.right.min
to_delete.data = new_value
to_delete.right.deleteNode(new_value, to_delete)
elif to_delete.left:
to_delete._mergeNodes(to_delete.left)
elif to_delete.right:
to_delete._mergeNodes(to_delete.right)
else:
if parent:
if parent.data > value:
parent.left = None
else:
parent.right = None
else:
self.data = None
Note, I don't use parent attribute, rather calculate it while deleting.
I have written a class called Node with certain functions to create a binary search tree. All of the functions work correctly except the function height() that is supposed to calculate the height of the BST. It returns a very small number compared to what I was expecting it too given that I haven't balanced the tree. The number I was expecting was close to N where N is the amount of numbers I have entered in the tree. Here is the code:
from __future__ import print_function
import random
class Node(object):
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 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:
return self, parent
def delete(self, data):
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
else:
self.data = None
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
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 compare_trees(self, node):
if node is None:
return False
if self.data != node.data:
return False
res = True
if self.left is None:
if node.left:
return False
else:
res = self.left.compare_trees(node.left)
if res is False:
return False
if self.right is None:
if node.right:
return False
else:
res = self.right.compare_trees(node.right)
return res
def print_tree(self):
if self.left:
self.left.print_tree()
print(self.data, end=" ")
if self.right:
self.right.print_tree()
def height(self, root):
if root is None:
return 0
else:
return max(self.height(root.left), self.height(root.right)) + 1
random.seed(3)
bst = Node(random.randint(1,1000))
for i in range(1,80000,1):
bst.insert(random.randint(1,1000))
print(bst.height(bst))
You are getting low answer because you are always inserting number from 1 to 1000 only so the numbers existing are always remains same and you are thinking you are inserting 1,80000 numbers but actually because of generating randomly the same numbers from 1 to 1000 you are actually inserting just 1000 values from 1 to 1000 maximum.
Wrong Code
bst = Node(random.randint(1,1000))
for i in range(1,80000,1):
bst.insert(random.randint(1,1000))
print(bst.height(bst))
Modification
bst = Node(random.randint(1,80000))
for i in range(1,80000,1):
bst.insert(random.randint(1,80000))
print(bst.height(bst))
Your code is working fine you can execute below code and check it with the image below
bst = Node(7)
list1 = [3,11,1,5,9,13,4,6,8,12,14,8.5]
for i in list1:
bst.insert(i)
print(bst.height(bst))
bst.print_tree()
Ouput
5
1 3 4 5 6 7 8 8.5 9 11 12 13 14
You should declare as sorted array to get maximum height for binary search tree.
but this may not work for larger numbers as 1000 or 10,000 . It will work fine for 500 elements because of your recursion for insertion may exceed the maximum recursion depth in python
UPTO 500
bst = Node(0)
list1 = list(range(1,500,1))
for i in list1:
bst.insert(i)
print(bst.height(bst))
OUTPUT
499
1000 elements
bst = Node(0)
list1 = list(range(1,500,1))
for i in list1:
bst.insert(i)
print(bst.height(bst))
OUTPUT
self.right.insert(data)
self.right = Node(data)
RecursionError: maximum recursion depth exceeded
My code is looking like this:
class Treenode:
def __init__(self,data,left=None,right=None):
self.data=data
self.left=left
self.right=right
def __str__(self):
return str(self.data)
def delete(self):
child=self.left
grandchild=child.right
print(grandchild)
if self.left == self.right==None:
return None
if self.left==None:
return self.right
if self.right==None:
return self.left
if grandchild:
while grandchild.right:
child = grandchild
grandchild = child.right
self.data = grandchild.data
child.right = grandchild.left
else:
self.left = child.left
self.data = child.data
return self
class Bintree:
def __init__(self):
self.root = None
def put(self,data):
if self.root == None:
self.root = Treenode(data)
return
p = self.root
while True:
if data < p.data:
if p.left == None:
p.left = Treenode(data)
return
else:
p = p.left
elif data > p.data:
if p.right == None:
p.right = Treenode(data)
return
else:
p = p.right
elif data == p.data:
return False
else:
return
def exists(self, data):
return finns(self.root, data)
def isempty(self):
return self.root == None
def height(self):
def hp(tree):
if tree is None:
return 0
else:
return 1 + max(hp(tree.left), hp(tree.right))
return hp(self.root)
def printTree(self):
skriv(self.root)
def remove(self, data):
if self.root and self.root.data == data: #self.root kanske inte behövs, undersök
self.root = self.root.delete()
return
else:
parent = self.root
while parent:
if data < parent.data:
child = parent.left
if child and child.data== data:
parent.left = child.delete()
return
parent = child
else:
child = parent.right
if child and child.data == data:
parent.right = child.delete()
return
parent = child
def skriv(tree):
if tree == None:
return
skriv(tree.left)
print(tree.data)
skriv(tree.right)
def finns(roten, key):
if roten == None:
return False
if key == roten.data:
return True
elif key < roten.data:
return finns(roten.left, key)
else:
return finns(roten.right, key)
Everything about my code is working, and I've simply added (see copied) the delete method and the remove method. Im desperately trying to understand the delete-method but I cannot understand it. I use this code to run the thing and see how the tree is implemented:
from labb8test import Bintree
from labb8test import Treenode
tree = Bintree()
tree.put(8)
tree.put(3)
tree.put(1)
tree.put(6)
tree.put(4)
tree.put(7)
tree.put(10)
tree.put(14)
tree.put(13)
tree.remove(6)
tree.printTree()
I'm trying to draw it on a paper and see, especially how the while-loop is working. According to my above code, I would think it is like this:
child = self.left (child=3) grandchild= child.right=self,left.right=6. If grandchild (yes, 6) while grandchild.right (yes, 7) child = grandchild, 3-->6 grandchild = child.right (is this even needed, 6--->6?) self.data=grandchild.data (8--->6) child.right = grandchild.left (6---->4) ??
But it cannot be like this, because then the while-loop would never end. Is there anyone who can help me understanding where I lose myself?
I recommend you this material from algorithm Princeton:
http://algs4.cs.princeton.edu/32bst/
The delete method is using this approach to delete a node from a bst.
Delete. We can proceed in a similar manner to delete any node that has
one child (or no children), but what can we do to delete a node that
has two children? We are left with two links, but have a place in the
parent node for only one of them. An answer to this dilemma, first
proposed by T. Hibbard in 1962, is to delete a node x by replacing it
with its successor. Because x has a right child, its successor is the
node with the smallest key in its right subtree. The replacement
preserves order in the tree because there are no keys between x.key
and the successor's key. We accomplish the task of replacing x by its
successor
in four (!) easy steps:
Save a link to the node to be deleted in t
Set x to point to its successor min(t.right)
Set the right link of x (which is supposed to point to the BST containing all the keys larger than x.key) to deleteMin(t.right), the
link to the BST containing all the keys that are larger than x.key
after the deletion.
Set the left link of x (which was null) to t.left (all the keys that are less than both the deleted key and its successor).
The end goal is to copy a node from one tree to another tree. I want to visit each node in a binary tree and return a node instance after a number of traverses. I cannot seem to figure out how to return a specific node. Every time the node returned matches the id of the root node since I pass the root node to the function.
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target):
global count
if target == count:
# print 'At target', '.'*25, target
return self
else:
if self.left is not None:
count += 1
self.left.randnode(target)
if self.right is not None:
count += 1
self.right.randnode(target)
If you're doing a DFS and counting iterations, you don't even need recursion, just a stack of places to try, and popping/pushing data.
def dfs(self,target):
count = 0
stack = [start]
while stack:
node = stack.pop()
if count == target:
return node
if node is None: # since we push left/right even if None
continue # stop pushing after we hit None node
stack.extend([self.left,self.right])
return -1 # ran out of nodes before count
Bonus points : swapping stack to a queue for BFS
Apart from that, you might want to pass the count as a parameter, like all self-respecting recursive calls, you can make this stateless ;-)
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target,count=0):
if target == count:
# print 'At target', '.'*25, target
return self
if self.left is not None:
return self.left.randnode(target,count + 1)
if self.right is not None:
return self.right.randnode(target,count + 1)
I implemented functions in Python to create a binary Search tree. It currently displays the nodes inorder and now I'm trying to get it to display in preorder.
I created the inorder function based on my notes, but I can't figure out how to get preorder to work correctly. Right now it displays the left-most nodes, but I can't get it to backtrack to the right nodes. I'll post my code with sample values. Any tips to get me on the right track would be greatly appreciated. Thanks!
I put all of my code and just the Preorder function code.
Full Python Code:
####
#
# Binary Search Tree
#
########
class Node:
#constructor
def __init__(self,data):
self.left = None
self.right = None
self.data = data
def insert(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)
#lookup node containing data
#data to lookup and parent nodes parent
#returns node and its parent if found, otherwise none
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:
return self,parent
#return the number of children (either 0,1,or 2)
def childrenCount(self):
children = 0
if(self.left):
children += 1
if(self.right):
children += 1
return children
#delete the node containing data
#data node content to be deleted
def delete(self,data):
node, parent = self.lookup(data)
if(node is not None):
childrenCount = node.childrenCount()
if(childrenCount == 0):
if(parent):
if(parent.left is node):
parent.left = None
else:
parent.right = None
del node
elif(childrenCount == 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:
parent = node
nextNode = node.right
while(nextNode.left):
parent = nextNode
nextNode = nextNode.left
node.data = nextNode.data
if(parent.left == nextNode):
parent.left = nextNode.right
else:
parent.right = nextNode.right
#display the tree via inorder
def displayInorder(self):
global dspList2
if(self.left):
self.left.displayInorder()
dspList2.append(self.data)
if(self.right):
self.right.displayInorder()
def preOrder(self):
global dspList
# dspList.append(self.data)
if(self.left):
print("Left.. appending",self.data)
dspList.append(self.data)
self.left.preOrder()
if (self.right is None):
print("No right.. append",self.data)
dspList.append(self.data)
dspList = []
dspList2 = []
def main():
global dspList2
global dspList
root = Node(8)
root.insert(3)
root.insert(10)
root.insert(1)
root.insert(6)
root.insert(4)
root.insert(7)
root.insert(14)
root.insert(13)
node,parent = root.lookup(6)
root.preOrder()
root.displayInorder()
print("Inorder:",dspList2)
print("Preorder:",dspList)
main()
Preorder function:
def preOrder(self):
global dspList
# dspList.append(self.data)
if(self.left):
print("Left.. appending",self.data)
dspList.append(self.data)
self.left.preOrder()
if (self.right is None):
print("No right.. append",self.data)
dspList.append(self.data)
Look at the explanations of the traversals on Wikipedia. Your solution is pretty simple, and is a clear translation of the explanation into code. Pre-order is only different from in-order in the order in which it recurses through the tree. Aside from that it's just as simple and straightforward. The required change to your code to get pre-order to work should likewise be a simple change from in-order to pre-order. The code below should work:
def inOrder(self):
global dspList2
if(self.left):
self.left.inOrder()
dspList2.append(self.data)
if(self.right):
self.right.inOrder()
def preOrder(self):
global dspList
dspList.append(self.data)
if(self.left):
self.left.preOrder()
if (self.right):
self.right.preOrder()
Note, rather than using globals, it would be safer and better style to delegate to a helper method that takes an accumulator:
def inOrder(self):
acc = []
self.inOrderAcc(self, acc)
return acc
def inOrderAcc(self, acc):
if (self.left):
self.left.inOrderAcc(acc)
acc.append(self.data)
if (self.right):
self.right.inOrderAcc(acc)