class TreeNode:
def __init__(self, _val):
self._val = _val
self._right = None
self._left = None
def search(self, val):
if self._val == val:
value = self._val
return self
if self._right is not None:
right = self._right._val
return self._right.search(val)
if self._left is not None:
left = self._left._val
return self._left.search(val)
EXAMPLE CALL:
a = TreeNode(3)
b = TreeNode(2)
c = TreeNode(4)
a.search(4)
So this just defines a treenode class, and search is supposed to take a val and simply search for it when called on the ROOT of a binary tree. Upon running a testcase, it sometimes returns the correct node, sometimes it returns false when I know it's in the tree. Anyone know what might be causing this inconsistency?
a.search(3) will work, because a's value is 3. For anything else, it shouldn't because you never allowed the nodes to connect up. For each of these nodes, at least based on the code given, don't have left or right branches. They are all initialized with None and never set to anything else. You should state the root branch's left and right and then it should work.
For instance, your class should have this:
def setLeft(self, node):
self._left = node
def setRight(self, node):
self._right = node
Then use it as such:
a = TreeNode(3)
b = TreeNode(2)
c = TreeNode(4)
a.setLeft(b)
a.setRight(c)
a.search(4)
Of course, you should change it depending on how you wanted to structure the tree.
Problems
If searching self._right returns None (the value isn’t anywhere on the right branch), your search will immediately return, never getting to traverse self._left. In fact, as long as self._right exists (is not None), the search method never traverses self._left.
Also, you need to make sure to link up your nodes before running! For example, a._left = c.
Solution
To fix this, I’d implement the search as follows:
def search(self, val):
if self._val = val:
return self
if self._right is not None:
right = self._right.search(val)
if right is not None:
return right # it’s okay to return now, we found something!
if self._left is not None: # if we’ve made it here, _right.search didn’t return
return self._left.search(val)
return None # it’s good practice to explicitly return
if self._right is not None:
It will stop the search and return the result of search right if with self._right, and no more search for the left side.
Here, I revised code and do test.
import random
class Node():
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def search(self, value):
if self.value == value:
return self
result = None
if self.left:
result = self.left.search(value)
if result:
return result
if self.right:
result = self.right.search(value)
return result
def __repr__(self):
return f'<Node value={self.value}>'
tree = {}
tree[0] = Node(0)
for i in range(1, 11):
while True:
parent = tree[random.choice(list(tree))]
position = random.choice([0, 1])
if (position==0 and parent.left) or (position==1 and parent.right):
continue
node = Node(i)
tree[i] = node
if position:
parent.right = node
else:
parent.left = node
break
root = tree[0]
for i in range(-5, 16):
if i in tree:
print(f'Value {i} found in {root.search(i)}')
else:
print(f'Value {i} not found')
Value -5 not found
Value -4 not found
Value -3 not found
Value -2 not found
Value -1 not found
Value 0 found in <Node value=0>
Value 1 found in <Node value=1>
Value 2 found in <Node value=2>
Value 3 found in <Node value=3>
Value 4 found in <Node value=4>
Value 5 found in <Node value=5>
Value 6 found in <Node value=6>
Value 7 found in <Node value=7>
Value 8 found in <Node value=8>
Value 9 found in <Node value=9>
Value 10 found in <Node value=10>
Value 11 not found
Value 12 not found
Value 13 not found
Value 14 not found
Value 15 not found
Related
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.
My homework needs me to sum all numbers under the given value in a BST. However, I had no idea how to do it. Appreciate for any help.
class BinarySearchTree:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def search(self, find_data):
if self.data == find_data:
return self
elif find_data < self.data and self.left != None:
return self.left.search(find_data)
elif find_data > self.data and self.right != None:
return self.right.search(find_data)
else:
return None
def get_left(self):
return self.left
def get_right(self):
return self.right
def set_left(self, tree):
self.left = tree
def set_right(self, tree):
self.right = tree
def set_data(self, data):
self.data = data
def get_data(self):
return self.data
def create_new_bst(lst):
#creates a new tree with root node 55, and then inserts all the
#remaining values in order into the BST
def sum_beneath(t, value):
# don't know how to do
t = create_new_bst([55, 24, 8, 51, 25, 72, 78])
result = sum_beneath(t, 72)
print('Sum beneath 72 =', result)# should show 'Sum beneath 72 = 78'
I'm very new to BST so I really have no idea on how to start and do this question.
def insert(self, new_data):#can I just call this function in 'create_new_bst'?
if self.data:
if new_data < self.data:
if self.left is None:
self.left = BinarySearchTree(new_data)
else:
self.left.insert(new_data)
elif new_data > self.data:
if self.right is None:
self.right = BinarySearchTree(new_data)
else:
self.right.insert(new_data)
else:
self.data = data
Ok, as this is an exercise, I won't fill everything, but I will try to give you an idea of how it should be done:
You need to create your tree, in a simple way you can do this:
def create_new_bst(lst):
tree = BinarySearchTree(tree[0])
# And then, using the insert method, which is correct, add your nodes in the tree
return tree
First, you need to find Your subtree with the root 72
# Replace the pass with the appropriate code
def find_subtree(tree, value):
if value == tree.data: # This is found yeah !
pass
if value > tree.data: # Ok, this is not our data, we should look recursively in one of the children (I will not tell you which one). Maybe we can use find_subtree reccursively?
pass
if value < tree.data: # Same as above, but maybe we should look in the other child
pass
raise ValueError("Not found value " + str(value)) # Nothing has been found.
Now, you found the tree with my_tree = find_subtree(t, 72), you should just sum the left tree (if it exists) and the right tree (if it exists)
def sum_beneath(t, value):
my_tree = find_subtree(t, value)
s = 0
if my_tree.left is not None:
s += my_tree.left.sum_tree()
if my_tree.right is not None:
s += my_tree.right.sum_tree()
return s
Let's define the sum_tree method (in the class)! :)
def sum_tree(self):
ans = self.data
# Add the left sum reccursively
# Add the right sum reccursively
return ans
I hope this will help you to understand the concept of BST. If you need help do not hesitate to comment
It's quite an interesting problem to find the sum of nodes under a specific value, we can think of this of something like searching and transversing problem, there can be various ways to do this, but I can think of this something like-
Doing a binary search for the node.
Doing an (In-order, Post-Order or Pre-order) transversal, and saving the results of the returned nodes' values i.e. summing them up.
the big O time complexity I can think of should be something like-
for an nth node in the BST, log(n) should be the search time, then for the transversal (In-order, Post-Order or Pre-order), it should be m-n, where (m is the total number of nodes)
therefore, the total will be, (log(n) + m - n) ~ O(M).
I have been working on trying to implement the function all_bigger below but I am not sure if there are flaws in my logic. To my understanding, BST's are organized having the smallest values on the left side so I would only need to check the left side of the BST. Is there a better way of writing this or is my code incorrect?
class BSTNode:
"""A node is a BST """
def __init__(self: 'BSTNode', item, left, right):
self.item, self.left, self.right = item, left, right
def all_bigger(self, value):
"""
>>> bst = BSTNode(5, BSTNode(4), BSTNode(6))
>>> all_bigger(bst, 2)
True
"""
while self.left:
if self.left > value:
self.value = self.left:
else:
return False
return True
Your code is almost correct, with some minor bugs. Corrected code:
class BSTNode:
"""A node is a BST """
def __init__(self, item, left = None, right = None):
self.item, self.left, self.right = item, left, right
def all_bigger(self, value):
"""
>>> bst = BSTNode(5, BSTNode(4), BSTNode(6))
>>> all_bigger(bst, 2)
True
"""
root = self
while(root!=None):
if root.item > value:
root = root.left
else:
return False
return True
bst = BSTNode(5, BSTNode(4,BSTNode(1, None, None),None), BSTNode(6,None,None)) # Returns False
print(bst.all_bigger(2))
IIUC your question is to see if all the nodes in the BST are bigger than a certain value.
A simple way to do is to find the node with the minimum value in the BST and compare it with the other value. The smallest node is going to be the left-most node.
A typical BST node looks like this
# A binary tree node
class Node:
# Constructor to create a new node
def __init__(self, key):
self.data = key
self.left = None
self.right = None
And yes, you're right. The tree does not need to be searched fully, ie, the right subtrees can be skipped. This is how you find the node with the minimum value.
def minValue(node):
current = node
# loop down to find the lefmost leaf
while(current.left is not None):
current = current.left
return current.data
This can be slightly tweaked to solve your problem
def all_bigger(node, val):
current = node
# loop down to find the lefmost leaf
while(current.left is not None):
current = current.left
# Check if the current node value is smaller than val
if current.data < val:
return False
return True
You need to update the node after every comparison. Kindly check the below code:
class BSTNode:
"""A node is a BST """
def __init__(self: 'BSTNode', item, left, right):
self.item, self.left, self.right = item, left, right
def all_bigger(self, value):
"""
>>> bst = BSTNode(5, BSTNode(4), BSTNode(6))
>>> all_bigger(bst, 2)
True
"""
while self.item:
if self.left > value:
self.item = self.left:
else:
return False
return True
All values in a subtree are greater than X if and only if
The value in the node is greater than X, and
All values in its left subtree (if it exists) are greater than X
Assuming an empty tree is None:
def all_bigger(self, value):
return self.value > value and self.left and self.left.all_bigger(value)
I have the task to perform some basic operations on Binary Search Trees and I'm not sure what is the clever way to do it.
I know that the usual way would be to write a class for the nodes and one for the tree so that I can build up my tree from given values and perform certain tasks on it. The thing is, I'm already getting the tree as a list and since BSTs are not unique, there won't come any good from it if I take each value and build the tree myself.
So... I'm getting a list like this:
11 9 2 13 _, 4 18 2 14 _, 2 10 _ 11 4, 14 16 4 _ _, 13 0 11 _ _ | 10 | 7
which means:
key value parent left right, ... | value1 | value2
So as you see the BST is given explicitly. My tasks are to do a level-print of the tree, return the path from root to value1, do a rotate-right operation on the subtree that has value1, then delete value1 and then insert value2.
What would be an efficient way to tackle this problem?
Here is one possible way of implementing the tree. Hope it helps. Though this contains insertions and popular traversals, not rotations or deletions.
Reference: http://www.thelearningpoint.net/computer-science/learning-python-programming-and-data-structures/learning-python-programming-and-data-structures--tutorial-20--graphs-breadth-and-depth-first-search-bfsdfs-dijkstra-algorithm-topological-search
'''
Binary Search Tree is a binary tree(that is every node has two branches),
in which the values contained in the left subtree is always less than the
root of that subtree, and the values contained in the right subtree is
always greater than the value of the root of the right subtree.
For more information about binary search trees, refer to :
http://en.wikipedia.org/wiki/Binary_search_tree
'''
#Only for use in Python 2.6.0a2 and later
from __future__ import print_function
class Node:
# Constructor to initialize data
# If data is not given by user,its taken as None
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
# __str__ returns string equivalent of Object
def __str__(self):
return "Node[Data = %s]" % (self.data,)
class BinarySearchTree:
def __init__(self):
self.root = None
'''
While inserting values in a binary search tree, we first check
whether the value is greater than, lesser than or equal to the
root of the tree.
We initialize current node as the root.
If the value is greater than the current node value, then we know that
its right location will be in the right subtree. So we make the current
element as the right node.
If the value is lesser than the current node value, then we know that
its right location will be in the left subtree. So we make the current
element as the left node.
If the value is equal to the current node value, then we know that the
value is already contained in the tree and doesn't need to be reinserted.
So we break from the loop.
'''
def insert(self, val):
if (self.root == None):
self.root = Node(val)
else:
current = self.root
while 1:
if (current.data > val):
if (current.left == None):
current.left = Node(val)
break
else:
current = current.left
elif (current.data < val):
if (current.right == None):
current.right = Node(val)
break
else:
current = current.right
else:
break
'''
In preorder traversal, we first print the current element, then
move on to the left subtree and finally to the right subree.
'''
def preorder(self, node):
if (node == None):
return
else:
print(node.data, end=" ")
self.preorder(node.left)
self.preorder(node.right)
'''
In inorder traversal, we first move to the left subtree, then print
the current element and finally move to the right subtree.
'''
#Important : Inorder traversal returns the elements in sorted form.
def inorder(self, node):
if (node == None):
return
else:
self.inorder(node.left)
print(node.data, end=" ")
self.inorder(node.right)
'''
In postorder traversal, we first move to the left subtree, then to the
right subtree and finally print the current element.
'''
def postorder(self, node):
if (node == None):
return
else:
self.postorder(node.left)
self.postorder(node.right)
print(node.data, end=" ")
tree = BinarySearchTree()
tree.insert(1)
tree.insert(9)
tree.insert(4)
tree.insert(3)
tree.insert(5)
tree.insert(7)
tree.insert(10)
tree.insert(0)
print ("Preorder Printing")
tree.preorder(tree.root)
print("\n\nInorder Printing")
tree.inorder(tree.root)
print("\n\nPostOrder Printing")
tree.postorder(tree.root)
Here is the implementation of Binary Search Tree with it's basic operations like insert node, find node
class Node:
def __init__(self,data):
self.left = None
self.right = None
self.data = data
class BST:
def __init__(self):
self.root = None
def set_root(self,data):
self.root = Node(data)
def insert_node(self,data):
if self.root is None:
self.set_root(data)
else:
n = Node(data)
troot = self.root
while troot:
if data < troot.data:
if troot.left:
troot = troot.left
else:
troot.left = n
break
else:
if troot.right:
troot = troot.right
else:
troot.right = n
break
def search_node(self,data):
if self.root is None:
return "Not found"
else:
troot = self.root
while troot:
if data < troot.data:
if troot.left:
troot = troot.left
if troot.data == data:
return "Found"
else:
return "Not found"
elif data > troot.data:
if troot.right:
troot = troot.right
if troot.data == data:
return "Found"
else:
return "Not found"
else:
return "Found"
tree = BST()
tree.insert_node(10)
tree.insert_node(5)
tree.insert_node(20)
tree.insert_node(7)
print(tree.root.data)
print(tree.root.left.data)
print(tree.root.right.data)
print(tree.root.left.right.data)
print(tree.search_node(10))
print(tree.search_node(5))
print(tree.search_node(20))
print(tree.search_node(7))
print(tree.search_node(12))
print(tree.search_node(15))
Output:
10
5
20
7
Found
Found
Found
Found
Not found
Not found
In this specific case I had success using a dictionary as a datatype to store the graph. The key is the node_key and the value is a list with the attributes of the node. In this way it is rather fast to find the needed nodes and all its attributes.
I'm just not sure if there is a way to make it reasonably faster.
I've created a BST. and now I want to find the height of the BST developed.
Here is my code for constructing the BST
class Node:
'''represents a new node in the BST'''
def __init__(self,key):
self.key=key
self.disconnect()
def disconnect(self):
self.left=None;
self.right=None;
self.parent=None;
def __str__(self):
return 'node with kay %s'%self.key
class BST:
def __init__(self):
self.root=None
def insert(self,t):
'''inserts a new element into the tree'''
if self.root is None:
self.root = Node(t)
else:
self._do_insert(self.root,t)
def _do_insert(self,parent,t):
if t > parent.key:
if parent.left is None:
parent.left = Node(t)
else:
self._do_insert(parent.left,t)
elif t < parent.key:
if parent.right is None:
parent.right = Node(t)
else:
self._do_insert(parent.right,t)
else:
# raise a KeyError or something appropriate?
pass
I've a list of numbers ([2,4,6,3,190,1,56 and so on]) via which this BST is constructed.
Now I want to find the height of the BST created. How can I do that?
EDIT
As per the solution provided I tried this :-
def create_bst(values):
'''Creates a BST and returns the BST created object'''
BSTobj = BST()
for i in values:
BSTobj.insert(i)
return BSTobj
def height_of_BST(bst):
'''Returns the height of the BST created'''
if bst == None: return 0
else: return 1 + max(height_of_BST(bst.left), height_of_BST(bst.right))
print height_of_BST(create_bst(unique_values))
And its not working. It pops up an error saying BST instance has no attribute 'left'
The height of a nonempty binary search tree is 1 + the height of its tallest subtree, or just 1 if it has no children. This translates pretty directly to a recursive algorithm. In pseudocode:
def height(bst):
if isempty(bst):
return 0
else:
return 1 + max(height(bst.left), height(bst.right))
The BST in your class is actually stored in BST.root not in BST. You need to modify your code to look at BST.root instead of BST.
Try:
def height(BST):
return actual_height(BST.root)
def actual_height(bst_node):
if bst_node is None:
return 0
else:
return 1 + max(actual_height(bst_node.left), actual_height(bst_node.right))
This defines a helper function that does the actual work but lets you just call height on the BST object. In the future, you might just want to only have a Node class because your BST class is basically just a wrapper around the root value.
The interpreter is complaining because you didn't check for cases where the node has no children. If a node has no children it heigth is -1
here a solution
def height(bst):
if bst == None :
return -1
else:
return 1 + max(height(bst.left), height(bst.right))
You can use hasattr to check if the object has the attr, if not you get to the end of the tree
def height(bst_node):
if not hasattr(bst_node, 'left') or not hasattr(bst_node, 'right'):
return 0
else:
return 1 + max(height(bst_node.left), height(bst_node.right))