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))
Related
I'm learning python and challenging myself by writing my own linked list from scratch. I'm using a tradition structure of an inner node class which holds a piece of data and a reference to the next node. Right now I'm trying to create a __repr__ method that returns a string representation of a node. The string it returns looks like this: "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
It works fine unless there's only 1 node in the list, which gave me the following error: AttributeError: 'NoneType' object has no attribute 'data'.
To get around this, I check first to see if there's only one node in the list, and I return the following string: "This node contains {0}. There is no next node." .format(self.data)
This is what my __repr__ method looks like right now:
def __repr__(self):
if MyLinkedList.get_size() == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
This is what the whole linked list class looks like so far:
class MyLinkedList(object):
head = None
size = None
def __init__(self):
self.size = 0
def get_head(self):
return self.head
def get_size(self):
return self.size
def is_empty(self):
if self.size == 0:
return True
else:
return False
def __repr__(self):
result = "["
curr = self.head
while curr != None:
if curr.next == None:
result += curr.data
break
result += curr.data
result += ", "
curr = curr.next
result += "]"
return result
def add_to_head(self, data):
new_node = MyLinkedList.Node(data)
if self.size == 0:
self.head = new_node
else:
new_node.next = self.head
self.head = new_node
self.size += 1
def delete_from_head(self):
if (self.size == 0):
self.head = None
else:
new_head = self.head.next
self.head = new_head
self.size =- 1
class Node():
next = None
def __init__(self, data):
self.data = data
def get_data(self):
return self.data
def get_next(self):
return self.next
def __repr__(self):
if MyLinkedList.get_size() == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
But now when I try to print the string representation of any node, it gives me the following error: TypeError: get_size() missing 1 required positional argument: 'self'
Is there any way to fix this issue? All I'm trying to do is to call my outer class's get_size() method in my inner node class, and check if that value is 1. Is there any other way to make it so my node's __repr__ method returns the string I want it to return when there's only one node in the list?
Also, if you spot other improvements you could make to my code, I would be glad to hear them.
You can only invoke get_size() on an instance of your MyLinkedList class. A node shouldn't know anything about the linked list class anyway. Just take advantage of the node's next pointer instead:
def __repr__(self):
suffix = "There is not next node" if self.next is None else "The next node is {}".format(self.next.data)
return "This node contains {}. {}.".format(self.data, suffix)
I believe you need to add a self argument into the get_size() method. Such as:
def __repr__(self):
if MyLinkedList.get_size(self) == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
class Node:
def __init__(self,data=None):
self.data=data
self.left_child=None
self.right_child=None
self.parent=None
self.root = None
class BinarySearchTree:
def __init__(self):
self.root=None
def add(self, data):
if self.root == None:
self.root = Node(data)
else:
self.add_helper(data, self.root)
def add_helper(self, data, cur_node):
if data < cur_node.data:
if cur_node.left_child == None:
cur_node.left_child = Node(data)
cur_node.left_child.parent = cur_node # set parent
else:
self.add_helper(data, cur_node.left_child)
elif data > cur_node.data:
if cur_node.right_child == None:
cur_node.right_child = Node(data)
cur_node.right_child.parent = cur_node # set parent
else:
self.add_helper(data, cur_node.right_child)
else:
print("data already in tree!")
def __len__(self):
if self.root is None:
return 0
else:
return (self.__len__(self.left_child) + 1 +self. __len__(self.right_child))
So i am trying to return the length of the binary search tree list, so i tried using the len method for my binary search tree class. However, this is not working correctly. I need it to be able to not take in any parameters, and just return an integer for the length of the binary search tree list. What am i missing and what am i doing wrong here?
You will need a helper function that takes a Node argument. Then do the recursion on the left and right of the node.
def __len__(self):
return self.tree_len(self.root)
def tree_len(self, node):
if node is None:
return 0
else:
return 1 + max(self.tree_len(node.right_child), self.tree_len(node.left_child))
I been looking over this Binary Tree code to figure out what it does:
class BinaryTree:
def __init__(self):
self.root = None
self.size = 0
# Insert element e into the binary search tree
# Return True if the element is inserted successfully
def insert(self, e):
if self.root == None:
self.root = self.createNewNode(e) # Create a new root
else:
# Locate the parent node
parent = None
current = self.root
while current != None:
if e < current.element:
parent = current
current = current.left
elif e > current.element:
parent = current
current = current.right
else:
return False # Duplicate node not inserted
# Create the new node and attach it to the parent node
if e < parent.element:
parent.left = self.createNewNode(e)
else:
parent.right = self.createNewNode(e)
self.size += 1 # Increase tree size
return True # Element inserted
# Create a new TreeNode for element e
def createNewNode(self, e):
return TreeNode(e)
# Return the size of the tree
def getSize(self):
return self.size
# Return true if the tree is empty
def isEmpty(self):
return self.size == 0
# Remove all elements from the tree
def clear(self):
self.root == None
self.size == 0
# Return the root of the tree
def getRoot(self):
return self.root
class TreeNode:
def __init__(self, e):
self.element = e
self.left = None # Point to the left node, default None
self.right = None # Point to the right node, default None
If I do the following code:
tree = BinaryTree()
tree.insert("500")
print(tree.getRoot)
print(tree.getRoot())
Result:
<bound method BinaryTree.getRoot of <__main__.BinaryTree object at 0x036E1E68>>
<__main__.TreeNode object at 0x036FA1F0>
I would not get the string "500" back even though its the root. Is there something missing from the getRoot function? I cannot find what is making the function being unable to return the string.
tree.getRoot is a method of the BinaryTree object.
And tree.getRoot() is calling the method and it returns a TreeNode object. If you want to get "500", you should get the element property of it.
So you can try the following code:
tree = BinaryTree()
tree.insert("500")
print(tree.getRoot().element)
which prints
500
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)
In my binary search tree I am making a depth function which will tell the user what is the depth of the tree they have insert. This function will be crucial for my unique delete function that deletes a node from the largest depth-sided node. I think I know where the problem is exactly but I'm not sure.
This is my error I keep receiving.
C:\Python33\python.exe "C:/Users/koopt_000/Desktop/College/Sophomore Semester 2/Computer Science 231/Chapter7/Test.py"
Traceback (most recent call last):
PRE-ORDER TRANSVERSE:
File "C:/Users/koopt_000/Desktop/College/Sophomore Semester 2/Computer Science 231/Chapter7/Test.py", line 19, in <module>
4
print("The max depth of the tree is,", a.height(tree),"nodes deep.")
2
File "C:\Users\koopt_000\Desktop\College\Sophomore Semester 2\Computer Science 231\Chapter7\BinarySearchTree.py", line 245, in height
1
3
7
6
5
10
None
IN-ORDER TRANSVERSE:
1
2
3
4
5
6
7
10
None
POST-ORDER TRANSVERSE:
1
return max(BST.height(root.left), BST.height(root.right)) + 1
3
TypeError: height() missing 1 required positional argument: 'root'
2
5
6
10
7
4
None
Process finished with exit code 1
Now I Think my problem arises in this section of code:
return max(BST.height(root.left), BST.height(root.right)) + 1
I believe this statement is what is causing it due to the fact that it is making me call the BST function which the height function is already in for it to 'work'. I simply tried "height.(root.left)", which did not work because it said there is no global variable height. Which isn't the case I don't believe.
Here is my full list of code for my function, starting with my tree node, then my BST file (main), and then my test code.
class TreeNode(object):
def __init__(self, data = None, left=None, right=None):
self.item = data
self.left = left
self.right = right
def __str__(self):
return str(self.item)
from TreeNode import TreeNode
class BST(object):
#------------------------------------------------------------
def __init__(self):
"""create empty binary search tree
post: empty tree created"""
self.root = None
self.size = 0
def delete(self, item):
"""remove item from binary search tree
post: item is removed from the tree"""
self.root = self._subtreeDelete(self.root, item)
#------------------------------------------------------------
def _subtreeDelete(self, root, item):
if root is None: # Empty tree, nothing to do
return None
if item < root.item: # modify left
root.left = self._subtreeDelete(root.left, item)
elif item > root.item: # modify right
root.right = self._subtreeDelete(root.right, item)
else: # delete root
if root.left is None: # promote right subtree
root = root.right
elif root.right is None: # promote left subtree
root = root.left
else:
# root node can't be deleted, overwrite it with max of
# left subtree and delete max node from the subtree
root.item, root.left = self._subtreeDelMax(root.left)
return root
def _subtreeDelMax(self, root):
if root.right is None: # root is the max
return root.item, root.left # return max and promote left subtree
else:
# max is in right subtree, recursively find and delete it
maxVal, root.right = self._subtreeDelMax(root.right)
return maxVal, root
def height(self, root):
if root is None:
return 0
else:
return max(BST.height(root.left), BST.height(root.right)) + 1
from BinarySearchTree import BST
from TreeNode import TreeNode
tree = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode (7, TreeNode(6),TreeNode(9)))
a = BST()
a._subtreeInsert(tree, 10)
a._subtreeInsert(tree, 5)
a._subtreeDelete(tree, 9)
print("PRE-ORDER TRANSVERSE:")
print(a.preOrder(tree))
print("IN-ORDER TRANSVERSE:")
print(a.inOrder(tree))
print("POST-ORDER TRANSVERSE:")
print(a.postOrder(tree))
print("The max depth of the tree is,", a.height(tree),"nodes deep.")
print("There are,", a.treeSize(tree),"nodes in this tree.")
Does anyone see the reason why my height function isn't working properly? Thanks!
Your function height is an instance method of the class BST, you need to call it via self and not with the class BST.
So in your particular case this is:
def height(self, root):
if root is None:
return 0
else:
return max(self.height(root.left), self.height(root.right)) + 1
However, your function height actually does not rely on any data associated directly with the search tree. self is only needed to continue the recursion. Thus, you could also turn it into static method via the staticmethod decorator:
#staticmethod
def height(root):
if root is None:
return 0
else:
return max(BST.height(root.left), BST.height(root.right)) + 1
Alternatively, you could also move the function out of the BST class entirely and get rid off BST.height and just call it via height.
From the code you posted, this holds for basically all functions of the BST class. I don't really see a need for it. You may only use the TreeNode and some top-level functions (without the BST class) in your python module to modify and interact with your tree.