I am writing a function to find height of node in an AVL tree and trying to implement it using python. This is the code I have written
class Node(object):
def __init__(self,data):
self.data=data
self.height=0
self.leftchild
self.rightchild
class AVL(object):
def __init__(self):
self.root=None
def calcHeight(self,node):
if not node:
return -1
return node.height
def calcBalance(self,node):
if not node:
return 0
return self.calcHeight(node.leftchild)-self.calcHeight(node.rightchild)
In this piece of code I am unable to understand if node.height is already a function defined in python and how is the height returned when we call the function recursively in self.calcheight. Since we have not written anything else apart from node.height
An AVL tree is a way of balancing a tree to ensure that the time to retrieve a node is approximately O(nlogn). The height of two subtrees can never be greater than one.
In the code above node.height is not an inbuilt function provided with Python.
You have defined a Node class, thus the node.height attribute refers to the height attribute in the Node class.
The calcHeight() method simply returns the height attribute of a constructed object of the Node class.
It is likely that the height is calculated and assigned to the height attribute when you insert a node into a binary tree.
"""
The BinaryTree node class
I have defined.
"""
class BinaryTree:
def __init__(self, root):
self.root = root
def insert(self, node):
# Call the private recursive insert method
self._insert(self.root, node)
"""
Inserts a node into the binary tree
from current_node as the root.
"""
def _insert(self, current_node, to_insert):
if (current_node.data < to_insert.data):
if (current_node.leftchild == None):
current_node.leftchild = to_insert
else:
self._insert(current_node.leftchild, to_insert)
else:
if (current_node.rightchild == None):
current_node.rightchild = to_insert
else:
self._insert(current_node.rightchild, to_insert)
# Work out the hieght of the current node after the insertion
left_child_height = 0
right_child_height = 0
if (current_node.leftchild):
left_child_height = current_node.leftchild.height
if (current_node.rightchild):
right_child_height = current_node.rightchild.height
# Calculate the height after the recursive call is made
current_node.height = max(left_child_height,
right_child_height) + 1
"""
Simply demonstrates that the height of each node is correctly
calculated.
"""
def in_order_print(self, node):
if (node.leftchild != None):
self.in_order_print(node.leftchild)
print("Node: " + str(node.data) + " Height: " + str(node.height))
if (node.leftchild != None):
self.in_order_print(node.rightchild)
Above I have defined an example of such a BinaryTree. The height is calculated after you insert the element. To understand how this works you need to understand the call stack in recursion. When you call a method the method call is placed onto a stack, when the method has finished executing it is popped off the stack.
Therefore in the _insert method above when the recursive call has finished (when the node has been placed at a leaf) we pop each recursive method call one by one. After popping off a method call we calculate the height by finding the greatest height of the left and right subtree and adding one.
height is an attribute of Node. You can look at Object Oriented Programming to understand this. calcHeight on the other hand is a method of the class AVL. When calculating the balance of the tree, it's called to calculate the difference between the height of the right child of the node and the left child. This will allow to test whether the tree is balanced at that Node or not. Here's a detailed page about AVL trees. I'm assuming the height in your code changes whenever a new Node is inserted, although it's not shown.
Related
I'm trying to learn python with some data-structure. I'm trying to create a binary tree.
So I've created a node like:
class Node(object):
def __init__(self,value,left,right):
self.value = value
self.left = left
self.right = right
Then I've created a tree like:
class Tree(object):
def __init__(self):
self.node = None
def insert(self,node,element):
if node == None:
node = Node(element,None,None)
return
elif element <= node.value:
self.insert(node.left, element)
else:
self.insert(node.right, element)
When I'm trying to insert element to the tree, it doesn't work. There is a stack call for that insert and the node==None is hitting and new Node is being created. But the Tree is not updating.
The line
node = Node(element,None,None)
will create a method local variable, which is only visible in the scope of that method. Thus it will not affect the Tree instance in any way. To create an instance variable, use
self.node = Node(element,None,None)
I am trying to understand binary trees, but doing so has brought me to confusion about how class instances interact, how does each instance link to another?
My Implementation:
class Node(object):
def __init__(self, key):
self.key= key
self.L = None
self.R = None
class BinaryTree(object):
def __init__(self):
self.root = None
def get_root(self):
return self.root
def insert(self, key):
if self.get_root()==None:
self.root = Node(key)
else:
self._insert(key, self.root)
def _insert(self, key, node):
if key < node.key:
if node.L == None:
node.L = key
else:
self._insert(key, Node(node.L))
if key > node.key:
if node.R == None:
node.R = key
else:
self._insert(key, Node(node.R))
myTree= BinaryTree()
A Scenario
So lets say I want to insert 10, I do myTree.insert(10) and this will instantiate a new instance of Node(), this is clear to me.
Now I want to add 11, I would expect this to become the right node of the root node; i.e it will be stored in the attribute R of the root node Node().
Now here comes the part I don't understand. When I add 12, it should become the child of the root nodes right child. In my code this creates a new instance of Node() where 11 should the be key and 12 should be R.
So my question is 2-fold: what happens to the last instance of Node()? Is it deleted if not how do I access it?
Or is the structure of a binary tree to abstract to think of each Node() connected together like in a graph
NB: this implementation is heavily derived from djra's implementation from this question How to Implement a Binary Tree?
Make L and R Nodes instead of ints. You can do this by changing the parts of your _insert function from this:
if node.L == None:
node.L = key
to this:
if node.L == None:
node.L = Node(key)
There is also a problem with this line:
self._insert(key, Node(node.L))
The way you're doing it right now, there is no way to access that last reference of Node() because your _insert function inserted it under an anonymously constructed node that has no parent node, and therefore is not a part of your tree. That node being passed in to your insert function is not the L or R of any other node in the tree, so you're not actually adding anything to the tree with this.
Now that we changed the Ls and Rs to be Nodes, you have a way to pass in a node that's part of the tree into the insert function:
self._insert(key, node.L)
Now you're passing the node's left child into the recursive insert, which by the looks of thing is what you were originally trying to do.
Once you make these changes in your code for both the L and R insert cases you can get to the last instance of Node() in your
10
\
11
\
12
example tree via myTree.root.R.R. You can get its key via myTree.root.R.R.key, which equals 12.
Most of you're questions come from not finishing the program; In your current code after myTree.insert(11) you're tree is setting R equal to a int rather than another Node.
If the value isn't found then create the new node at that point. Otherwise pass the next node into the recursive function to keep moving further down the tree.
def _insert(self, key, node):
if key < node.key:
if node.L == None:
node.L = Node(key)
else:
self._insert(key, node.L)
if key > node.key:
if node.R == None:
node.R = Node(key)
else:
self._insert(key, node.R)
P.S. This isn't finished you're going to need another level of logic testing incase something is bigger than the current Node.key but smaller than the next Node.
I have my tree data structure as below:
class Node(object):
def __init__(self, data):
self.data = data
self.children = []
def add_child(self, obj):
self.children.append(obj)
Then I created a method to accomplish it.
def replace(node, newNode):
if node.data == 1:
node = newNode
return
else:
for i in xrange(0, len(node.children)):
replace(node.children[i], newNode)
This method is called just like that:
replace(mytree,newNode)
Since it is recursive call, I think the object get destroyed and the assignment does not happen.
I tried it manually as:
mytree.children[0].children[0] = newNode
then the tree is correctly updated. How can I achieve it using my method above?
The assignment node = newNode doesn't do what you want. It doesn't replace the object you know as node with newNode everywhere. It just rebinds the local variable name node to point to the same object as the other local name newNode. Other references to the first node (such as in its parent's children list) will be unchanged.
To actually do what you want requires more subtlety. The best approach is often often not to replace the node at all, but rather to replace its contents. That is, set node.data and node.children to be equal to newNode.data and newNode.children and leave node in place. This only fails to work properly if there are other references to node or newNode and you want them to work properly after the replacement.
The alternative is to do the replacement in the parent of the node you're looking for. This won't work at the top of your tree, so you'll need special logic to handle that situation.
def replace(node, newNode):
if node.value == 1:
raise ValueError("can't replace the current node this way")
for index, child in enumerate(node.children):
if child.data == 1:
node.children[index] = newNode
return True
if replace(child, newNode):
return True
return False
I've also added some extra logic to stop the recursive processing of the tree when the appropriate node has been found. The function will return True if a replacement has been made, or False if the right data value was not found.
For example, a tree like this:
5
/ \
3 6
/ \
7 2
print(tree.branchLenSum())
will be 1+1+2+2=6
Tree class:
class BinaryTree:
# Constructor, takes in new key value
def __init__(self, myKey):
self.key = myKey
self.leftChild = None
self.rightChild = None
# Returns root key value
def getRootValue(self):
return self.key
# Changes root key value
def setRootValue(self, newKey):
self.key = newKey
# Returns reference to left child
def getLeftChild(self):
value=None
if self.leftChild!=None:
value=self.leftChild
return value
# Returns reference to right child
def getRightChild(self):
value=None
if self.rightChild!=None:
value = self.rightChild
return value
def insertLeftChild(self, childKey):
newNode = BinaryTree(childKey)
newNode.leftChild = self.leftChild
self.leftChild = newNode
# Inserts key as right child. Existing right child becomes new right child
# of new key
def insertRightChild(self, childKey):
newNode = BinaryTree(childKey)
newNode.rightChild = self.rightChild
self.rightChild = newNode
The tree I have built for the example:
tree=BinaryTree(5)
tree.insertLeftChild(3)
tree.insertRightChild(6)
nodeA=tree.getLeftChild()
nodeA.insertLeftChild(7)
nodeA.insertRightChild(2)
What I have so far:
def branchLenSum(self):
rounds=0
if self.getLeftChild() ==None and self.getRightChild()==None:
return rounds+rounds+1
else:
rounds+=rounds+1
if self.getLeftChild()!=None:
rounds+=self.getLeftChild().branchLenSum()
if self.getRightChild()!=None:
rounds+=self.getRightChild().branchLenSum()
return rounds
My idea is that every time travel to next node, counter adds 1+counter itself. I think this will get all the length sum.
Okay, so the reason why you only get a result of 5 is rather simple: What you are doing is count the nodes. So in your case, you have 5 nodes, so the result is 5.
If you want to get the internal path length, then I believe you will have to keep track of the current depth while navigating through the tree. You can do this simply by using an optional parameter.
def branchLenSum(self, depth = 0):
rounds = depth
if self.leftChild:
rounds += self.leftChild.branchLenSum(depth + 1)
if self.rightChild:
rounds += self.rightChild.branchLenSum(depth + 1)
return rounds
In this case, whenever we navigate down to a child, we increase the current depth by one. And when counting the branch length of a node, we start at the depth.
Btw. note that officially, the internal path length is defined as the length for only the internal nodes, i.e. not leaves. The method above counts every node including leaves. If you want to follow the official definiton, you will have to add a leaf-check at the beginning and return 0 for leaves.
Some other things:
The methods getLeftChild and getRightChild do effectively nothing. You assign None to the return value, then check if the left/right child is None and if that’s not the case you assign the child to the return value and return it.
So essentially, you are returning self.leftChild/self.rightChild; there’s no need to actually look at the value and check for None.
In Python, you usually don’t use accessor or mutator methods (getters/setters); you just access the underlying property itself. This makes the methods getLeftChild, getRightChild, getKey and setKey redundant.
Checking for None with != None or == None is an antipattern. If you want to check if, for example a child is not None, just do if child. And if you want to check if it is not set (i.e. not None) just do if not child.
Basically I want to be able to have each node of type tree have a Data field and a list of branches. This list should contain a number of objects of type Tree.
I think I have the actual implementation of the list down, but I get strange behavior when I try using the getLeaves method. Basically it calls itself recursively and never returns, and the way that happens is somehow the second node of the tree gets it's first branch set as itself (I think).
class Tree:
"""Basic tree graph datatype"""
branches = []
def __init__(self, root):
self.root = root
def addBranch (self, addition):
"""Adds another object of type Tree as a branch"""
self.branches += [addition]
def getLeaves (self):
"""returns the leaves of a given branch. For leaves of the tree, specify root"""
print (len(self.branches))
if (len(self.branches) == 0):
return self.root
else:
branchSum = []
for b in self.branches:
branchSum += b.getLeaves()
return (branchSum)
Your 'branches' variable is a class member, not an instance member. You need to initialize the 'branches' instance variable in the constructor:
class Tree:
"""Basic tree graph datatype"""
def __init__(self, root):
self.branches = []
self.root = root
The rest of your code looks good.
Is self.root the parent of said tree? In that case, getLeaves() should return self if it has no branches (len(self.branches)==0) instead of self.root as you have it there. Also, if you do have child branches you should include self within branchSum.
Possible solution (your source code with small changes):
class Tree:
def __init__(self, data):
"""Basic tree graph datatype"""
self.data = data
self.branches = []
def addBranch (self, addition):
"""Adds another object of type Tree as a branch"""
self.branches.append(addition)
def getLeaves (self):
"""returns the leaves of a given branch. For
leaves of the tree, specify data"""
if len(self.branches) == 0:
return self.data
else:
branchSum = []
for b in self.branches:
branchSum.append(b.getLeaves())
return branchSum
## Use it
t0 = Tree("t0")
t1 = Tree("t1")
t2 = Tree("t2")
t3 = Tree("t3")
t4 = Tree("t4")
t0.addBranch(t1)
t0.addBranch(t4)
t1.addBranch(t2)
t1.addBranch(t3)
print(t0.getLeaves())
Output:
[['t2', 't3'], 't4']
Remarks:
Looks that some formatting is broken in your code.
Not really sure if this is what you want. Do you want all the leaves in one level of the list? (If so the source code has to be adapted.)