I am inexperienced in python.
I'm doing a college activity and I wrote a class to find the height of a binary tree.
But when I'm going to call the function recursively I have the message:
NameError Traceback (most recent call last)
<ipython-input-5-109fbd93416e> in <module>
7 raiz.insert(3)
8
----> 9 heigth(raiz)
NameError: name 'heigth' is not defined
The function is :
def heigth(self, n:"Node")-> int:
if n:
return 1
else:
left = heigth(n.left)
right = heigth(n.right)
if(left < right):
return right + 1
else:
return left + 1
All code:
from typing import List
class Node:
def __init__(self, key, left:"Node"=None, right:"Node"=None):
self.key = key
self.left = left
self.right = right
def print_tree(self):
"""
Prints the tree from the current node
"""
if self.left:
self.left.print_tree()
print(self.key, end=" ")
if self.right:
self.right.print_tree()
def insert(self, key) -> bool:
"""
Insert a node in the tree that has the key "key"
"""
if key < self.key:
if self.left:
return self.left.insert(key)
else:
self.left = Node(key)
return True
elif key > self.key:
if self.right:
return self.right.insert(key)
else:
self.right = Node(key)
return True
else:
return False
def search(self, key) -> bool:
"""
Returns true if the key exists in the tree
"""
if key < self.key:
if self.left:
return self.left.search(key)
elif key > self.key:
if self.right:
return self.right.search(key)
else:
return True
return False
def to_sorted_array(self, arr_result:List =None) -> List:
"""
Returns a vector of the ordered keys.
arr_result: Parameter with the items already added.
"""
if(arr_result == None):
arr_result = []
if self.left:
self.left.to_sorted_array(arr_result)
arr_result.append(self.key)
if self.right:
self.right.to_sorted_array(arr_result)
return arr_result
def max_depth(self,current_max_depth:int=0) -> int:
"""
calculates the greatest distance between the root node and the leaf
current_max_depth: Value representing the longest distance so far
when calling for the first time, there is no need to use it
"""
current_max_depth = current_max_depth +1
val_left,val_right = current_max_depth,current_max_depth
if self.left:
val_left = self.left.max_depth(current_max_depth)
if self.right:
val_right = self.right.max_depth(current_max_depth)
if(val_left>val_right):
return val_left
else:
return val_right
def position_node(self, key, current_position:int=1) -> int:
"""
Returns the position of the desired node in the tree
current_position: represents the position of the tree at that moment
when calling for the first time, there is no need to use it
"""
if key < self.key:
if self.left:
return self.left.position_node(key, current_position*2)
elif key > self.key:
if self.right:
return self.right.position_node(key, current_position*2+1)
else:
return current_position
def heigth(self, n:"Node")-> int:
if n:
return 1
else:
left = heigth(n.left)
right = heigth(n.right)
if(left < right):
return right + 1
else:
return left + 1
You are using method of a class, but when you refer to it just as a heigth it looks for the method not in a class of the object, but on a module level where it can't find it, hence the error.
Try replacing heigth calls with self.heigth, so that method of a class is called.
You cannot call the function in classes you must call the class first using self than the function:
left = self.height(n.left)
right = self.height(n.right)
You can make height a static method of Node.
You have to call the height method with the raiz instance as argument.
class Node:
# ...
#staticmethod
def height(n:"Node")-> int:
return 1 + max(self.height(n.left), self.height(n.right)) if n else 0
raiz.insert(3)
print(Node.height(raiz))
Or make it a recursive method
class Node:
# ...
def _height(self, n:"Node") -> int:
return n.height() if n else 0
def height(self) -> int:
return 1 + max(self._height(self.left), self._height(self.right))
raiz.insert(3)
print(raiz.height())
Related
I'm unable to identify where I'm going wrong with my AVL implementation for balancing an existing binary search tree. I'm not getting any errors but my binary search tree does not come out to be properly balanced. After insertion, my binary search tree looks like (it would be prudent here to mention that my display_keys method gives us a visualization that is rotated by 90 degrees):
∅
The-Dreamers
∅
Saint-Laurent
∅
Pierrot-le-Fou
∅
Contempt
Cold-War
Before-Sunrise
∅
Basic-Instinct
∅
This is correct as it seems be to be following the rules for a BST.
But after calling the BalanceTree() method on my binary search tree, I seem to get:
∅
The-Dreamers
Saint-Laurent
Pierrot-le-Fou
Contempt
Cold-War
Before-Sunrise
Basic-Instinct
∅
which as you can see, is not Balanced. But wait, and here's the catch, if I call BalanceTree() again, the tree comes out to be perfectly balanced and isBSTBalanced returns True also. After the second call to BalanceTree(), our tree looks like:
∅
The-Dreamers
Saint-Laurent
Pierrot-le-Fou
Contempt
Cold-War
Before-Sunrise
Basic-Instinct
∅
I am adding the complete source code for my BST class for clarity and if somebody wants to execute the code, but I have added a comment (#Addition of new methods for AVL starts here) in the BST class to indicate where the methods for AVL start. You need only concern yourself with them. I would like for you help me pinpoint what exactly is going wrong in my code.
class BST:
class TreeNode:
def __init__(self, key, value, left=None, right=None, parent=None):
self.key = key
self.value = value
self.left = left
self.right = right
self.parent = parent
self.height = 1
def __init__(self):
self.root = None
self.size = 0
def __len__(self):
return self.size
def insert(self, key, value):
if self.root == None:
self.root = self.TreeNode(key, value)
else:
self._insert(key, value, self.root)
self.size += 1
def _insert(self, key, value, curr_node):
if key < curr_node.key:
if curr_node.left is not None:
self._insert(key, value, curr_node.left)
else:
curr_node.left = self.TreeNode(key, value, parent=curr_node)
elif key > curr_node.key:
if curr_node.right is not None:
self._insert(key, value, curr_node.right)
else:
curr_node.right = self.TreeNode(key, value, parent=curr_node)
def search(self, key):
if self.root:
found = self._search(key, self.root)
if found:
return found.value
else:
return None
else:
return None
def _search(self, key, curr_node):
if not curr_node:
return None
elif curr_node.key == key:
return curr_node
elif key < curr_node.key:
return self._search(key, curr_node.left)
else:
return self._search(key, curr_node.right)
def find_min(self):
curr = self.root
while curr.left is not None:
curr = curr.left
return curr
def find(self, node):
curr = node
while curr.left is not None:
curr = curr.left
return curr
def delete(self, key):
node_to_remove = self._search(key, self.root)
if node_to_remove.left is None and node_to_remove.right is None:
#Then we identify this as a leaf node
if node_to_remove is node_to_remove.parent.left:
#Setting the parent's reference to this to None
node_to_remove.parent.left = None
elif node_to_remove is node_to_remove.parent.right:
node_to_remove.parent.right = None
#2nd Case --> Two child
elif node_to_remove.left and node_to_remove.right:
minimum = self.find(node_to_remove.right)
self.delete(minimum.key) #We will still have a ref to this node afterwards
node_to_remove.key, node_to_remove.value = minimum.key, minimum.value
#3rd Case -> One child
else:
if node_to_remove.left:
node_to_remove.left.parent = node_to_remove.parent
node_to_remove.parent.left = node_to_remove.left
elif node_to_remove.right:
node_to_remove.right.parent = node_to_remove.parent
node_to_remove.parent.right = node_to_remove.right
def traversal(self, root):
res = []
if root:
res = self.traversal(root.left)
res.append(root)
res = res + self.traversal(root.right)
return res
def inorder_traversal(self, root):
if root:
self.inorder_traversal(root.left)
print(root.key)
self.inorder_traversal(root.right)
#Addition of new methods for AVL starts here
def display_keys(self, node, space='\t', level=0):
"""
Allows us to visualize the tree (albiet rotated by 90 degrees)
"""
# print(node.key if node else None, level)
# If the node is empty
if node is None:
print(space*level + '∅')
return
# If the node is a leaf
if node.left is None and node.right is None:
print(space*level + str(node.key))
return
# If the node has children
self.display_keys(node.right, space, level+1)
print(space*level + str(node.key))
self.display_keys(node.left,space, level+1)
def height(self):
return self._height(self.root)
def _height(self, curr_node):
if curr_node is None:
return -1 #since we are counting number of edges, we will return -1
else:
return 1 + max(self._height(curr_node.left), self._height(curr_node.right))
def isBSTBalanced(self):
return self._isBSTBalanced(self.root)
def _isBSTBalanced(self, curr_node):
if curr_node is None:
return True
hleft_subtree = self._height(curr_node.left)
hright_subtree = self._height(curr_node.right)
if hleft_subtree - hright_subtree in [-1,0,1]:
return self._isBSTBalanced(curr_node.left) and self._isBSTBalanced(curr_node.right)
else:
return False
def balance_factor(self):
if self.root is not None:
return self._balance_factor(self.root)
else:
return 0
def _balance_factor(self, curr_node):
if curr_node is None:
return
hleft_subtree = self._height(curr_node.left)
hright_subtree = self._height(curr_node.right)
b_factor = hleft_subtree - hright_subtree
return b_factor
def BalanceTree(self):
if self.isBSTBalanced() == False:
return self._rebalance(self.root)
def _rebalance(self, curr_node):
if curr_node is None:
return None
curr_node.left = self._rebalance(curr_node.left)
curr_node.right = self._rebalance(curr_node.right)
curr_node.height = 1 + max(self._height(curr_node.left), self._height(curr_node.right))
#print(curr_node.height)
if self._balance_factor(curr_node) > 1 and self._balance_factor(curr_node.left) >= 0:
#left heavy subtree
return self._rotate_right(curr_node)
if self._balance_factor(curr_node) < -1 and self._balance_factor(curr_node.right) <= 0:
#right heavy subtree
return self._rotate_left(curr_node)
if self._balance_factor(curr_node) < 0 and self._balance_factor(curr_node.right) > 0:
self._rotate_right(curr_node.right)
return self._rotate_left(curr_node)
if self._balance_factor(curr_node) > 0 and self._balance_factor(curr_node.left) < 0:
self._rotate_left(curr_node.left)
return self._rotate_right(curr_node)
return curr_node
def _rotate_left(self, oldRoot):
newRoot = oldRoot.right #the newRoot is the right child of the previous root
oldRoot.right = newRoot.left #replacing right child of the old root with the left child of the new
if newRoot.left is not None:
newRoot.left.parent = oldRoot
newRoot.parent = oldRoot.parent
if oldRoot == self.root:
self.root = newRoot
else:
if oldRoot.parent.left is oldRoot: #Checking isLeftChild
oldRoot.parent.left = newRoot
else:
oldRoot.parent.right = newRoot
newRoot.left = oldRoot
oldRoot.parent = newRoot
oldRoot.height = 1 + max(self._height(oldRoot.left), self._height(oldRoot.right))
newRoot.height = 1 + max(self._height(newRoot.left), self._height(newRoot.right))
return newRoot
def _rotate_right(self, oldRoot):
newRoot = oldRoot.left #the newRoot is the left child of the previous root
oldRoot.left = newRoot.right #replacing left child of the old root with the right child of the new
if newRoot.right is not None:
newRoot.right.parent = oldRoot
newRoot.parent = oldRoot.parent
if oldRoot == self.root:
self.root = newRoot
else:
if oldRoot.parent.right is oldRoot: #Checking isRightChild
oldRoot.parent.right = newRoot
else:
oldRoot.parent.left = newRoot
newRoot.right = oldRoot
oldRoot.parent = newRoot
oldRoot.height = 1 + max(self._height(oldRoot.left), self._height(oldRoot.right))
newRoot.height = 1 + max(self._height(newRoot.left), self._height(newRoot.right))
return newRoot
if __name__ == '__main__':
obj = BST()
obj.insert('Basic-Instinct', 0)
obj.insert('The-Dreamers', 1)
obj.insert('Saint-Laurent', 2)
obj.insert('Pierrot-le-Fou', 3)
obj.insert('Contempt', 4)
obj.insert('Before-Sunrise', 5)
obj.insert('Cold-War', 8)
obj.display_keys(obj.root) #displays a visual representation of our tree, albeit rotated by 90 degrees
print()
print("isBSTBalanced:", obj.isBSTBalanced())
obj.BalanceTree()
print("isBSTBalanced:", obj.isBSTBalanced()) #After executing BalanceTree(), isBSTBalanced still returns False
print()
obj.display_keys(obj.root)
Progress: Revamped _isBSTBalanced method so that it visits every node recursively and not just the root node. The final outcome, however, remains the same.
Progress: I was able to identify one of the major issues being that while I was calling _rotate_left and _rotate_right methods in the _rebalance method, I was not returning them. In addition to this, I was not recursively visiting the left and right subtrees of curr_node, which was initially set to the root of the tree, to be able to traverse the tree in a bottom up manner. I have resolved this too. I have added a display_keys method which allows us to visualize the tree, albeit rotated by 90 degrees. I'm updating the code and prompt in this post accordingly. The problem that still remains is that I have to call the BalanceTree() method more than once in some cases for isBSTBalanced to return True.
Below is a binary search tree which has a root node, a left node and a right node.
The code works but I want to display this binary search tree so that i can see every node in layer...
Here is the code...
class Node:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
class Binary_search_tree:
def __init__(self):
self.root=None
def insert(self,value):
if self.root==None:
self.root=Node(value)
else:
self.insert_after_root(value)
def insert_after_root(self, value):
if value > self.root.value:
self.root.left = Node(value)
elif value < self.root.value:
self.root.right = Node(value)
bst = Binary_search_tree()
bst.insert(4)
bst.insert_after_root(2)
bst.insert_after_root(8)
Your implementation has some problems:
The tree can only have 3 nodes, since you never create a grand-child of the root, but always make the new node either the root, or one of its children
left/right are reversed: you should insert smaller values to the left.
In the main program code, you should only use the insert method, never the insert_after_root.
Here is a correction of your implementation, based on recursion (putting a method on the Node), and an additional set of methods for producing a string representation, 90° tilted (with the root displayed at the left).
class Node:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
def insert_after(self, value):
if value < self.value:
if self.left:
self.left.insert_after(value)
else:
self.left = Node(value)
elif value > self.value:
if self.right:
self.right.insert_after(value)
else:
self.right = Node(value)
else:
raise ValueError("this tree doesn't accept duplicates")
def __repr__(self):
lines = []
if self.right:
found = False
for line in repr(self.right).split("\n"):
if line[0] != " ":
found = True
line = " ┌─" + line
elif found:
line = " | " + line
else:
line = " " + line
lines.append(line)
lines.append(str(self.value))
if self.left:
found = False
for line in repr(self.left).split("\n"):
if line[0] != " ":
found = True
line = " └─" + line
elif found:
line = " " + line
else:
line = " | " + line
lines.append(line)
return "\n".join(lines)
class Binary_search_tree:
def __init__(self):
self.root=None
def insert(self,value):
if self.root==None:
self.root=Node(value)
else:
self.root.insert_after(value)
def __repr__(self):
return repr(self.root)
bst = Binary_search_tree()
bst.insert(4)
bst.insert(2)
bst.insert(8)
bst.insert(3)
bst.insert(5)
bst.insert(7)
bst.insert(10)
print(str(bst))
Here is a simple implementation of binary search tree. In addition I recommend you to don't use == operator with None, instead of that use is, here why should I avoid == None
class Node:
def __init__(self,key):
self.left = None
self.right = None
self.value = key
def insert(root,node):
if root is None:
root = node
else:
if root.value < node.value:
if root.right is None:
root.right = node
else:
insert(root.right, node)
else:
if root.left is None:
root.left = node
else:
insert(root.left, node)
def left_right(root):
if root:
left_right(root.left)
print(root.value) # that shows your tree
left_right(root.right)
tree = Node(20)
insert(tree,Node(30))
insert(tree,Node(10))
insert(tree,Node(40))
insert(tree,Node(90))
left_right(tree)
for this case, it ask me to do a indirect recursion. for "def count_less" function. which mean count the number of nodes which is less than the given value.For my code I will have attributeError. Can anyone help me to check my code?
class BST:
"""A Binary Search Tree."""
def __init__(self, container=[]):
"""(BST, list) -> NoneType
Initialize this BST by inserting the items from container (default [])
one by one, in the order given.
"""
# Initialize empty tree.
self.root = None
# Insert every item from container.
for item in container:
self.insert(item)
def __str__(self):
"""(BST) -> str
Return a "sideways" representation of the values in this BST, with
right subtrees above nodes above left subtrees and each value preceded
by a number of TAB characters equal to its depth.
"""
if self.root:
return self.root._str("")
else:
return ""
def count_less(self, item):
"""(BST, object) -> int
Return the number of items in this BST that are strictly less than
item.
"""
if self.root:
return self.root.count_less(item)
else:
return 0
I need to write the function body in this "_BSTNode" class, and call the class method in above "BST" class.
class _BSTNode:
"""A node in a BST."""
def __init__(self, item, left=None, right=None):
"""(_BSTNode, object, _BSTNode, _BSTNode) -> NoneType
Initialize this node to store item and have children left and right.
"""
self.item = item
self.left = left
self.right = right
def _str(self, indent):
"""(_BSTNode, str) -> str
Return a "sideways" representation of the values in the BST rooted at
this node, with right subtrees above nodes above left subtrees and each
value preceded by a number of TAB characters equal to its depth, plus
indent.
"""
if self.right:
right_str = self.right._str(indent + "\t")
else:
right_str = ""
if self.left:
left_str = self.left._str(indent + "\t")
else:
left_str = ""
return right_str + indent + str(self.item) + "\n" + left_str
def count_less(self: '_BSTNode', item: object) -> int:
"""
Return the number of items in the BST rooted at this node that are
strictly less than item.
"""
if not self.item:
return 0
elif item <= self.item:
if self.left:
return 1 + self.left.count_less(item)
return 0
elif self.item < item:
if self.left and self.right:
return 1 + self.left.count_less(item) + self.right.count_less(item)
elif self.left and not self.right:
return 1 + self.left.count_less(item)
elif self.right and not self.left:
return 1 + self.right.count_less(item)
else:
return 1
And this is my example I put in and error in my output.
>>> t = BST(container=[5,1,2,3,4,6,7,8,9])
>>> t.count_less(10)
You can depend on the falsy nature of None and use if statements to check to see if the child nodes exist. I'm pretty sure that the below code won't return the correct answer: what happens if self.item == item? It's possible for the children of both the left and right children to have items that are less than the item argument.
elif item < self.item:
if self.left:
return 1 + self.left.count_less(item)
return 1
elif item > self.item:
if self.right:
return 1 + self.right.count_less(item)
return 1
I want to find the size of the tree with a given node which will be stated like this
print bst.get("B")
However, when I tried to print out, it keeps stating that "it only accept 1 argument but 2 is given"
Sorry, can someone help me out, as I'm quite new to this.
the brief code is:
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size(temp.left) + self.size(temp.right)
def size2(self,n):
if n is None:
return 0
else:
return 1 + self.size2(n.left) + self.size2(n.right)
The full code:
import os
import pygraphviz as pgv
from collections import deque
class BST:
root=None
def put(self, key, val):
self.root = self.put2(self.root, key, val)
def put2(self, node, key, val):
if node is None:
#key is not in tree, create node and return node to parent
return Node(key, val)
if key < node.key:
# key is in left subtree
node.left = self.put2(node.left, key, val)
elif key > node.key:
# key is in right subtree
node.right = self.put2(node.right, key, val)
else:
node.val = val
# node.count = 1 + self.size2(node.left) + self.size2(node.right)
return node
# draw the graph
def drawTree(self, filename):
# create an empty undirected graph
G=pgv.AGraph('graph myGraph {}')
# create queue for breadth first search
q = deque([self.root])
# breadth first search traversal of the tree
while len(q) <> 0:
node = q.popleft()
G.add_node(node, label=node.key+":"+str(node.val))
if node.left is not None:
# draw the left node and edge
G.add_node(node.left, label=node.left.key+":"+str(node.left.val))
G.add_edge(node, node.left)
q.append(node.left)
if node.right is not None:
# draw the right node and edge
G.add_node(node.right, label=node.right.key+":"+str(node.right.val))
G.add_edge(node, node.right)
q.append(node.right)
# render graph into PNG file
G.draw(filename,prog='dot')
os.startfile(filename)
def createTree(self):
self.put("F",6)
self.put("D",4)
self.put("C",3)
self.put("B",2)
self.put("A",1)
self.put("E",5)
self.put("I",9)
self.put("G",7)
self.put("H",8)
self.put("J",10)
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size(temp.left) + self.size(temp.right)
def size2(self,n):
if n is None:
return 0
else:
return 1 + self.size2(n.left) + self.size2(n.right)
class Node:
left = None
right = None
key = 0
val = 0
def __init__(self, key, val):
self.key = key
self.val = val
bst = BST()
bst.createTree()
bst.drawTree("demo.png")
##print bst.size("D")
I get a stack overflow with your code. I think you need to use size2 in your size method:
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size2(temp.left) + self.size2(temp.right)
Personally, I would maybe not call the method size2, but that's a matter of taste (and style). Also, the key seems to be unused?
How do I make a function that returns the number of nodes in a tree that have two children?
My class code is as follows:
class RefBinaryTree:
def __init__(self, data, left=None, right=None):
self.key = data
self.left = left
self.right = right
def insert_left(self, value):
self.left = RefBinaryTree(value, left=self.left)
def insert_right(self, value):
self.right = RefBinaryTree(value, right=self.right)
def get_left_subtree(self):
return self.left
def get_right_subtree(self):
return self.right
def set_value(self, new_value):
self.key = new_value
def get_value(self):
return self.key
def create_string(self, indent):
string = str(self.key) + '---+'
if self.left:
string += '\n(l)' + indent + self.left.create_string(indent + ' ')
if self.right:
string += '\n(r)' + indent + self.right.create_string(indent + ' ')
return string
def __str__(self):
return self.create_string(' ')
I'm guessing it would be best to use recursion. Any hints or helpful links would be awesome. Thanks.
It's really quite simple to count two-child nodes recursively. If you return a number with each function call (zero as the base case) you can simply add 1 every time you find a two-child node:
def findDoubleNodes(tree):
if tree == None or (tree.left == None and tree.right == None):
# base case
return 0
elif tree.left <> None and tree.right <> None:
# both have children, so add one to our total and go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right) + 1
else:
# only one child, so only go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right)
Inputting a RefBinaryTree returns the number of nodes with two children. An example:
x = RefBinaryTree(1)
x.insert_left(5)
x.left.insert_left(6)
x.left.insert_right(7)
x.left.right.insert_left(8)
x.left.right.insert_right(9)
x.left.right.right.insert_right(10)
The (lazily) created tree looks like this:
1
/
5
/ \
6 7
/ \
8 9
\
10
And findDoubleNodes(x) returns 2, as only two nodes (5 and 7) have two children.
Additionally, adding a left child to node 9 (x.left.right.right.insert_left(11)) has the expected result, returning 3.
This should do:
def countNodes(tree):
if tree is None:
return 0
left = tree.get_left_subtree()
rght = tree.get_right_subtree()
return (0 if left is None or rght is None else 1) \
+ countNodes(left) + countNodes(rght)