Find if all nodes in BST are greater than a item - python

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)

Related

Printing a Binary Tree using Inorder Traversal

I want to print a Binary Tree using Inorder Traversal, I have these functions and I'm wondering how I would go about writing a function to use them to print the binary tree. Any help massively appreciated.
def inorder(self):
if not self.is_empty():
for p in self._subtree_inorder(self.root()):
yield p
def _subtree_inorder(self, p):
if self.left(p) is not None:
for other in self._subtree_inorder(self.left(p)):
yield other
yield p
if self.right(p) is not None:
for other in self._subtree_inorder(self.right(p)):
yield other
def positions(self):
return self.inorder()
Here two possible solutions in Python, given the following binary search tree.
20
/ \
10 30
/ \
35 40
\
37
Recursive traversal
The recursion ends whenever a node is null.
Call inorder_rec() first for the left subtree, then print the value of the current node, then print it for the right subtree.
Traversal using generator
The approach is more or less the same (not surprisingly, as the algorithm is the same). We first need to yield all the result from the left subtree, then we yield the value of the current node and last but not least we yield the keys from the right subtree.
All together
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.key = key
class Bst:
def __init__(self):
self.root = None
def insert(self, key):
self.root = self.insert_rec(self.root, key)
def insert_rec(self, node, key):
if node is None:
return Node(key)
if key == node.key:
print(f"Key {key} already present! Ignoring value!")
return node
if key <= node.key:
node.left = self.insert_rec(node.left, key)
else:
node.right = self.insert_rec(node.right, key)
return node
def inorder(self):
self.inorder_rec(self.root)
def inorder_rec(self, node):
# end of recursion if current node is None
if node is None:
return
# indorder means: left subtree, then own value, then right subtree
self.inorder_rec(node.left)
print(node.key)
self.inorder_rec(node.right)
def inorder_with_generator(self):
# yield from inorder_genreator()
yield from self.inorder_generator(self.root)
def inorder_generator(self, node):
# nothing to yield if node is None
if node is not None:
for node_data in self.inorder_generator(node.left):
yield node_data
yield node.key
for node_data in self.inorder_generator(node.right):
yield node_data
tree = Bst()
tree.insert(20)
tree.insert(10)
tree.insert(30)
tree.insert(40)
tree.insert(35)
tree.insert(37)
tree.inorder()
print(list(tree.inorder_with_generator()))
Expected output:
10
20
30
35
37
40
[10, 20, 30, 35, 37, 40]
To avoid having to provide the root node as an argument every time I have added two functions which always start the traversal at the root node without the need to supply any parameters.

Why I am seeing None data after deleting the smallest or largest item from Binary Search Tree?

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.

Check whether the given value is in the binary search tree

This is the code I was given and I need to make changes to it. The function looks for the value in the given BST and returns True if target is in the tree. Here's the code:
def member_prim(tnode, target):
"""
Purpose:
Check if target is stored in the binary search tree.
Pre-Conditions:
:param tnode: a treenode with the BST property
:param target: a value
Post-Conditions:
none
Return
:return: True if target is in the tree
"""
if tnode is None:
return False
elif target < tnode.data:
right = tnode.right
return member_prim(right, target)
else:
tnode.right = tnode.left
return member_prim(tnode.right, target)
I made few changes to the code and here's what I did:
if tnode is None:
return False
elif tnode.data is not target:
return False
elif target > tnode.data:
right = tnode.right
return member_prim(right, target)
else:
tnode.right = tnode.left
return member_prim(tnode.right, target)
Still it throws Attribute Error, saying object 'treenode' has no attribute 'data'.
Here's treenode class:
class treenode(object):
def __init__(self, data, left=None, right=None):
"""
Purpose:
Create a new treenode for the given data.
Pre-conditions:
data: Any data value to be stored in the treenode
left, right: Another treenode (or None, by default)
Post-condition:
none
"""
self.data = data
self.left = left
self.right = right
Please let me know where I went wrong and how can I fix it.
class treenode(object):
def __init__(self, data, left=None, right=None):
"""
Purpose:
Create a new treenode for the given data.
Pre-conditions:
data: Any data value to be stored in the treenode
left, right: Another treenode (or None, by default)
Post-condition:
none
"""
self.data = data
self.left = left
self.right = right
def trav(root, target):
if not root:
return False
if root.data == target:
return True
if root.data > target:
return trav(root.left, target)
if root.data < target:
return trav(root.right, target)
This code will find you the if the target value is present or not.

How can I sum all nodes under a given value in binary search tree?

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).

Implementing Binary Search Tree (Python)

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.

Categories