Sum of length of the branches in a tree - python

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.

Related

Finding height of AVL tree?

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.

Delete in Binary Search tree using python

Good day, I'm working on a home work that requires me to delete a target value, value, from a binary search tree, tnode; it also requires me to return a tuple. I adapted the code I found for Python 2. But its giving me an Attribute Error whenever I try to run it. I've been trying to figure out how to fix these to no avail. Can anybody point out what I'm doing wrong? Here's what I have.
def delete_prim(tnode, value):
"""
Delete a value from the binary search tree.
Preconditions:
:param tnode: a TreeNode with the BST property
:param value: a value
Postconditions:
If the value is in the tree, it is deleted.
If the value is not there, there is no change to the tree.
Return
:return: tuple (flag, tree)
flag is True if deletion succeeded;
tree is the resulting without the value
flag is False if the value was not in the tree,
tree returned unchanged
"""
def minimum_value(tnode):
"""This function searches for the minimum value in the tree."""
cur = tnode
while cur.left is not None:
cur = cur.left
return cur
if tnode is None:
return False, tnode
if value < tnode.data:
tnode.left = delete_prim(tnode.left,value)
elif value > tnode.data:
tnode.right = delete_prim(tnode.right,value)
elif tnode.left is None:
return tnode.right
elif tnode.right is None:
return tnode.left
else:
tnode.data = minimum_value(tnode.right)
tnode.right = delete_prim(tnode.right, tnode.data)
return False, tnode
Edit: I got the code working for some part, but I found a problem that it wont delete a node that has children.
Edit: Tree node implementation.
class TreeNode(object):
def __init__(self, data, left=None, right=None):
"""
Create a new TreeNode for the given data.
Pre-conditions:
data: Any data value to be stored in the KVTreeNode
left: Another KVTreeNode (or None, by default)
right: Another KVTreeNode (or None, by default)
"""
self.data = data
self.left = left
self.right = right

Binary Tree: How Do Class Instances Link?

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.

How to replace a subtree in python

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.

Python tree operations

I need to implement (or just use) a tree data structure on which I can perform:
1. Child additions at any specified position. The new child can itself be a big tree (need not be a singleton)
2. Subtree deletions and moving (to another node in the same tree)
3. Common traversal operations.
4. Access parent from child node.
First, is there any module I can use for this?
Second, if I were to implement this by myself, I've this concern:
When I do tree manipulations like moving subtrees, removing subtrees or adding new subtrees, I only wish to move the "references" to these tree nodes. For example, in C/C++ these operations can be performed by pointer manipulations and I can be assured that only the references are being moved.
Similarly, when I do tree "movements" I need to move only the reference - aka, a new copy of the tree should not be created at the destination.
I'm still in a "pointers" frame of thinking, and hence the question. May be, I don't need to do all this?
You can easily make your own tree with operator overloading. For example, here is a basic class with __add__ implemented :
class Node(object):
def __init__(self, value):
self.value = value
self.child = []
def add_child(self, child):
self.child.append(child)
def __add__(self, element):
if type(element) != Node:
raise NotImplementedError("Addition is possible only between 2 nodes")
self.value += element.value # i didn't get if you have to add also childs
return self # return the NODE object
So to answer to your second question, there is a python trick here. In __add__ you return self. Then, this return True:
a = Node(1)
b = Node(2)
print a is a + b
If you use a + b, this will modify the value a. a and b are, in fact, pointers. Then if you pass it as argument in a function, and you modify them in the function, the a and b instances will be modified. There is two different way to avoid this (maybe more, but this is the two i use) :
The first one is to directly modify the definition of __add__ :
def __add__(self, element):
# .../...
value = self.value + element.value
return Node(value) # you may add rows in order to copy childs
The second one is to add a copy method :
def copy(self):
# .../...
n = Node(self.value)
n.child = self.child[:] # Copy the list, in order to have 2 different instance of this list.
return n
This will allow you to do something like c = a.copy() + b and the assertion c is a will be false.
Hope I answered to your question.
Thi is an example for you:
class BinaryTree:
def __init__(self,rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None
def insertLeft(self,newNode):
if self.leftChild == None:
self.leftChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t
def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.rightChild = self.rightChild
self.rightChild = t
def getRightChild(self):
return self.rightChild
def getLeftChild(self):
return self.leftChild
def setRootVal(self,obj):
self.key = obj
def getRootVal(self):
return self.key

Categories

Resources