Python tree operations - python

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

Related

Creating the list that corresponds to the preorder path on binary trees

I was reading the following link. I want to create a method that instead of printing the elements, it returns the associated list. This is what I did:
class Node(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
class BinaryTree(object):
def __init__(self, root):
self.root = Node(root)
def preorder_sequence(self, current_node):
"""Helper method - use this to create a
recursive print solution."""
s = []
if current_node is not None:
s.append(current_node.value)
self.preorder_sequence(current_node.left)
self.preorder_sequence(current_node.right)
return s
This method is just returning the root element.
The main issue is that although your method returns a list, the recursive calls ignore that returned list, and so each call can only return a list that has at the most one element in it. Remember that s is a local name, and each execution context of your function has its own version of it. Your code really needs to capture the returned list as that is the only access the caller has to the list that the recursive execution created.
You could correct your code like this:
def preorder_sequence(self, current_node):
if current_node is not None:
return ([current_node.value]
+ self.preorder_sequence(current_node.left)
+ self.preorder_sequence(current_node.right))
else:
return []
Here you see how the lists that are created by the recursive calls are used to build a larger list (using +).
Now I should add that it is more pythonic to create a generator for this purpose: this means the function does not create a list, but just "spits out" the values in the requested order, and it becomes the responsibility of the caller to do something with them (like putting them in a list or printing them):
def preorder_sequence(self, current_node):
if current_node is not None:
yield current_node.value
yield from self.preorder_sequence(current_node.left)
yield from self.preorder_sequence(current_node.right)
I would even move the recursive part of this method to the Node class:
class Node:
# ...
def preorder_sequence(self):
yield self.value
if self.left:
yield from self.left.preorder_sequence()
if self.right:
yield from self.right.preorder_sequence()
And then in the BinaryTree class it would become:
class BinaryTree:
# ...
def preorder_sequence(self): # No more extra argument here
if self.root:
return self.root.preorder_sequence()
The caller can for instance do these things now:
tree = BST()
# ... populate the tree
# ...
# And finally create the list
lst = list(tree.preorder_sequence())
Or, just print using the * operator:
tree = BST()
# ... populate the tree
# ...
print(*tree.preorder_sequence())

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.

Need help understanding python simple linked list program

Below is a simple linked list program, I know how a linked list works conceptually ( adding, removing, etc) but I am finding it hard to understand how it works from an object oriented design perspective.
Code:
class Node():
def __init__(self,d,n=None):
self.data = d
self.next_node = n
def get_next(self):
return self.next_node
def set_next(self,n):
self.next_node = n
def get_data(self):
return self.data
def set_data(self,d):
self.data = d
class LinkedList():
def __init__(self,r = None):
self.root = r
self.size = 0
def get_size(self):
return self.size
def add(self,d):
new_node = Node(d,self.root)
self.root = new_node
self.size += 1
def get_list(self):
new_pointer = self.root
while new_pointer:
print new_pointer.get_data()
new_pointer = new_pointer.get_next()
def remove(self,d):
this_node = self.root
prev_node = None
while this_node:
if this_node.get_data() == d:
if prev_node:
prev_node.set_next(this_node.get_next())
else:
self.root = this_node
self.size -= 1
return True
else:
prev_node = this_node
this_node = this_node.get_next()
return False
def find(self,d):
this_node = self.root
while this_node:
if this_node.get_data() == d:
return d
else:
this_node = this_node.get_next()
return None
myList = LinkedList()
myList.add(5)
myList.add(8)
myList.add(12)
myList.get_list()
I have couple questions here..
How is it storing the values. As far as I understand each variable can hold one value. So how does data / next_node hold multiple values. And does next_node hold the memory location of the next node?
new_pointer.get_data() How is new_pointer able to access get_data()? Don't we need to have an instance to access methods of Node?
This question may be silly, but I am quiet new to object oriented programming. If someone can answer these questions or post an external link addressing these questions it would be really helpful.
Thanks in advance.
next_node is an instance of Node and so it has its own data field. next_node is a reference to the node object, which is some memory address (however it is not a C-like pointer, as you don't need to dereference it or anything).
I'm assuming you are talking about get_list(). new_pointer is an instance of Node. (unless it is None, in which case you would never get into the get_data() call). When you do an add, you create this instance of Node and set root to it. Then in get_list you set new_pointer to root.
myList.root is storing one value only that is the root of the list. See initially when you do:
myList = LinkedList()
in memory myList.root = None (according to __init__ of LinkedList). Now:
myList.add(1)
Then this statement is called:
new_node = Node(d,self.root) #Note here self.root = None
and then:
def init(self,d,n=None):
self.data = d
self.next_node = n
So our list is : 1--> None.Now:
myList.add(2)
then again this statement is called:
new_node = Node(d,self.root) #Note here self.root has some value
now a new node object is created and its next is assigned to myList.root.
So our list becomes : 2-->1--> None
Going in similar fashion whole list is assigned.
Key thing to note here is that myList.root is always storing the top most node which in turn holds the next node and so on.
For your second question, it is quite clear from above explaination that always the object of class node is available to myList.root which in turn has next_node which is again an object of 'node'. So they all have access to 'get_data()' method.

Sum of length of the branches in a tree

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.

How to print leaves of a tree implemented as a list of subtrees in Python?

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

Categories

Resources