I am trying to implement Binary Search Tree operations in python. As of now, I have written some code to add nodes to this search tree (sorted).
Here's what I've in my code:
class TreeNode:
def __init__(self, data):
self.data = data
self.lLink = None
self.rLink = None
class BinaryTree:
def __init__(self):
self.root = None
def AddNode(self, data):
if self.root is None:
self.root = TreeNode(data)
else:
if data < self.root.data:
if self.root.lLink is None:
self.root.lLink = TreeNode(data)
else:
AddNode(self.root.lLink, data)
else:
if self.root.rLink is None:
self.root.rLink = TreeNode(data)
else:
AddNode(self.root.rLink, data)
def InOrder(self, head):
if self.root.lLink is not None:
InOrder(self.root.lLink)
print self.root.data,
if self.root.rLink is not None:
InOrder(self.root.rLink)
myTree = BinaryTree()
myTree.AddNode(15)
myTree.AddNode(18)
myTree.AddNode(14)
How do I test if my AddNode() method is correct? I know the algorithm but just to be sure.
What I was thinking of is to create an InOrder() method and try to print elements through this InOrder traversal. As a result, my data added to the tree should be displayed in sorted order. If it is displayed in sorted order, I'll be sure that both my AddNode() and InOrder() methods are correct.
Your BinaryTree class is faulty, changing the order of insertions to
myTree.AddNode(14)
myTree.AddNode(18)
myTree.AddNode(15)
raises an error - NameError: global name 'AddNode' is not defined.
This is because in the lines, AddNode(self.root.rLink, data) and AddNode(self.root.lLink, data) you seem to be calling the AddNode function on instances of TreeNode which is not possible. I fixed up some of the errors in your code and it should work great now.
class TreeNode:
def __init__(self, data):
self.data = data
self.lLink = None
self.rLink = None
class BinaryTree:
def __init__(self):
self.root = None
def AddNode(self, data):
if self.root is None:
self.root = TreeNode(data)
else:
self.AddHelper(data, self.root)
def AddHelper(self, data, startingPoint):
if data < startingPoint.data:
if startingPoint.lLink is None:
startingPoint.lLink = TreeNode(data)
else:
self.AddHelper(data, startingPoint.lLink)
else:
if startingPoint.rLink is None:
startingPoint.rLink = TreeNode(data)
else:
self.AddHelper(data, startingPoint.rLink)
def InOrder(self):
self.InOrderHelper(self.root)
def InOrderHelper(self, startingPoint):
if startingPoint is None:
return
self.InOrderHelper(startingPoint.lLink)
print startingPoint.data,
self.InOrderHelper(startingPoint.rLink)
Output Test :
>>> myTree = BinaryTree()
>>> myTree.AddNode(14)
>>> myTree.AddNode(18)
>>> myTree.AddNode(15)
>>> myTree.InOrder()
14 15 18
Inserting can be a little tricky, especially because the function is a part of the tree itself. So, you call the insert function on the tree, but specifying a starting point. This defaults to root, so you can leave the argument when you call the function.
Also, I think you are a little unclear about how self works in a function. You cannot pass it as an argument to the function, which is what it seems you have done.
class TreeNode:
def __init__(self, data):
self.data = data
self.rLink = None
self.lLink = None
class BinaryTree:
def __init__(self):
self.root = None
def AddNode(self, data, node=None):
if not node :
node = self.root
if self.root is None:
self.root = TreeNode(data)
else:
if data < node.data:
if node.lLink is None:
node.lLink = TreeNode(data)
else:
self.AddNode(data, self.root.lLink)
else:
if node.rLink is None:
node.rLink = TreeNode(data)
else:
self.AddNode(data, self.root.rLink)
def InOrder(self, head):
if head.lLink is not None:
self.InOrder(head.lLink)
print head.data,
if head.rLink is not None:
self.InOrder(head.rLink)
myTree = BinaryTree()
myTree.AddNode(14)
myTree.AddNode(15)
myTree.AddNode(18)
myTree.InOrder(myTree.root)
Testing the insert function with an in-order traversal is the best approach.
This should work. You are not going down the tree if you use self.root.lLink every time.
Optionally, you could write one more line of code to check if the output is indeed in ascending order.
Related
I've been messing around learning about objects and classes, and as I finally felt that I managed to wrap my head around how to construct a Binary Search Tree in python, I ran into an issue. Here's my code:
class node:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
def add(self,current,value):
if self.root == None:
self.root = node(value)
else:
if value < current.value:
if current.left == None:
current.left = node(value)
else:
self.add(current.left,value)
if value > current.value:
if current.right == None:
current.right = node(value)
else:
self.add(current.right,value)
def visit(self,node):
print(node.value)
def inorder(self,current):
self.inorder(current.left)
self.visit(current)
self.inorder(current.right)
Tree = BST()
root = node(2)
Tree.root = root
Tree.add(Tree.root,7)
Tree.inorder(Tree.root)
after running the code, I got an error: AttributeError: 'NoneType' object has no attribute 'left'.
The error comes out of the inorder function, as apparently the value I'm inserting into the function is an object without a type.
as you can see, the root of the tree is a node object, so it should have the attribute "left" as per coded in the class. I would really appreciate if someone could help me with what I'm getting wrong.
Thanks in advance!
EDIT: I should note that I checked whether the root is a node with the isinstance(), function, and indeed it returned True.
I think you sould check current is not None before access its left and right.
def inorder(self, current):
if current:
self.inorder(current.left)
self.visit(current)
self.inorder(current.right)
Your design has some problems. The Tree should be the only object that knows about the root. You shouldn't have to pass the root in. Notice how this is simpler to use:
class node:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
def add(self,value,current=None):
if not self.root:
self.root = node(value)
return
if not current:
current = self.root
if value < current.value:
if not current.left:
current.left = node(value)
else:
self.add(value,current.left)
else:
if not current.right:
current.right = node(value)
else:
self.add(value, current.right)
def visit(self,node):
print(node.value)
def inorder(self,current=-1):
if current == -1:
current = self.root
if not current:
return
self.inorder(current.left)
self.visit(current)
self.inorder(current.right)
Tree = BST()
Tree.add(2)
Tree.add(7)
Tree.inorder()
can someone help me out with this? I implemented a binary search tree data structure in python and I wrote a BST_height() function to calculate the height of the tree. But when I ran my code, It gave me an error saying 'self is not defined'. I know why the error is showing up but can you suggest some other way to run the BST_height function with the root node
class Node:
def __init__(self, data=None):
self.data = data
self.left = None
self.right = None
class BinarySearchTree:
def __init__(self):
self.root = Node()
def display(self):
print('''
{}
/ \\
{} {}
/ \\ / \\
{} {} {} {}
BINARY TREE
'''.format(tree.root.data, tree.root.left.data, tree.root.right.data, tree.root.left.left.data, tree.root.right.right.data, tree.root.left.right.data, tree.root.right.left.data))
def checkRoot(self):
if self.root.data != None:
return 'Root node exists'
else:
return 'Root node doesn\'t exists'
def insert(self, data):
newNode = Node(data)
if self.root.data == None:
# creating the root node
self.root = newNode
else:
self.insertNode(data, self.root)
def insertNode(self, data, curNode):
if data < curNode.data:
if curNode.left == None:
curNode.left = Node(data)
else:
self.insertNode(data, curNode.left)
elif data > curNode.data:
if curNode.right == None:
curNode.right = Node(data)
else:
self.insertNode(data, curNode.right)
else:
print('The value already exists ha ha')# funny
def BST_height(self, node):
if node == None:
return -1
leftHeight = height(node.left)
rightHeight = height(node.right)
return max(leftHeight, rightHeight) + 1
tree = BinarySearchTree()
tree.insert(30)# root node
tree.insert(24)
tree.insert(45)
tree.insert(90)
tree.insert(18)
tree.insert(28)
tree.insert(40)
tree.display()
# getting an error here
# I know self.root can\'t be used outside the class but can you suggest some other way tree.BST_height(self.root)
There are 2 problems with the code:
Self is something you use inside of class functions. Outside you can simply use the object variable. like so:
tree.BST_height(tree.root)
BST_height function has a small error in it. It calls height instead of self.BST_height:
def BST_height(self, node):
if node == None:
return -1
leftHeight = self.BST_height(node.left)
rightHeight = self.BST_height(node.right)
return max(leftHeight, rightHeight) + 1
You might want to read this for clarity on the whole self topic.
I am currently having trouble with a AVL tree in Python 3. I have wrote out the source code that I am following which is on a video, but it is acting strange and I have no idea why.
Here is the code:
class Node(object):
def __init__(self, data, parentNode):
self.data = data
self.parentNode = parentNode
self.rightChild = None
self.leftChild = None
self.balance = 0
def insert(self, data, parentNode):
if data < self.data:
if not self.leftChild:
self.leftChild = Node(data, parentNode)
else:
self.leftChild.insert(data, parentNode)
else:
if not self.rightChild:
self.rightChild = Node(data, parentNode)
else:
self.rightChild.insert(data, parentNode)
return parentNode
def traverseInOrder(self):
if self.leftChild:
self.leftChild.traverseInOrder()
print(self.data)
if self.rightChild:
self.rightChild.traverseInOrder()
def getMax(self):
if not self.rightChild:
return self.data
else:
return self.rightChild.getMax()
def getMin(self):
if not self.leftChild:
return self.data
else:
return self.leftChild.getMin()
class BalancedTree(object):
def __init__(self):
self.rootNode = None
def insert(self, data):
parentNode = self.rootNode
if self.rootNode == None:
parentNode = Node(data, None)
self.rootNode = parentNode
else:
parentNode = self.rootNode.insert(data, self.rootNode)
self.rebalanceTree(parentNode)
def rebalanceTree(self, parentNode):
self.setBalance(parentNode)
if parentNode.balance < -1:
if self.height(parentNode.leftChild.leftChild) >= self.height(parentNode.leftChild.rightChild):
parentNode = self.rotateRight(parentNode)
else:
parentNode = self.rotateLeftRight(parentNode)
elif parentNode.balance > 1:
if self.height(parentNode.rightChild.rightChild) >= self.height(parentNode.rightChild.leftChild):
parentNode = self.rotateLeft(parentNode)
else:
parentNode = self.rotateRightLeft(parentNode)
if parentNode.parentNode is not None:
self.rebalanceTree(parentNode.parentNode)
else:
self.rootNode = parentNode
def rotateLeftRight(self, node):
print("Rotation left right....")
node.leftChild = self.rotateLeft(node.leftChild)
return self.rotateRight(node)
def rotateRightLeft(self, node):
print("Rotation right left....")
node.rightChild = self.rotateRight(node.rightchild)
return self.rotateLeft(node)
def rotateLeft(self, node):
print("Rotate left....")
b = node.rightChild
b.parentNode = node.parentNode
node.rightChild = b.leftChild
if node.rightChild is not None:
node.rightChild.parentNode = node
b.leftChild = node
node.parentNode = b
if b.parentNode is not None:
if b.parentNode.rightChild == node:
b.parentNode.rightChild = b
else:
b.parentNode.leftChild = b
self.setBalance(node)
self.setBalance(b)
return b
def rotateRight(self, node):
print("Rotation right....")
b = node.leftChild
b.parentNode = node.parentNode
node.leftChild = b.rightChild
if node.leftChild is not None:
node.leftChild.parentNode = node
b.rightChild = node
node.parentNode = b
if b.parentNode is not None:
if b.parentNode.rightChild == node:
b.parentNode.rightChild = b
else:
b.parentNode.leftChild = b
self.setBalance(node)
self.setBalance(b)
return b
def setBalance(self, node):
node.balance = (self.height(node.rightChild) - self.height(node.leftChild))
def height(self, node):
if node == None:
return -1
else:
return 1 + max(self.height(node.leftChild), self.height(node.rightChild))
As I test this, this is what happens.
I create a tree:
tree = BalancedTree()
I then try to insert 3 intergers.
tree.insert(4)
tree.insert(2)
Now when I enter the third interger.
tree.insert(3)
I get this output without calling any functions.
Rotation left right....
Rotate left....
Rotation right....
That is what happens. I try to traverse the tree. I receive this error.
Traceback (most recent call last): File "", line 1, in
tree.traverseInOrder() AttributeError: 'BalancedTree' object has no attribute 'traverseInOrder'
Yet the video I am following his code works fine. I am lost as I have relooked over the code to see if I made a mistake somewhere and doesn't seem like I have. What am I missing? In his code there is no traverseInOrder function for the tree itself. Yet he is able to call it and run it just fine. Can someone explain why this is happening? Please and thank you.
The immediate problem is that your indentation is incorrect for the usage you give. As posted, class node consists of only this:
class Node(object):
def __init__(self, data, parentNode):
self.data = data
self.parentNode = parentNode
self.rightChild = None
self.leftChild = None
self.balance = 0
Then you have a few independent functions, followed by your trivial balancedTree class, and a few other independent functions.
However, this wouldn't allow you to insert a value the way you've done it, so it appears that the posted code is not what produced the output you give.
UPDATE
I fixed the indentation. The code appears to properly insert and balance the tree of 3 nodes. However, there is no error. The line of code you cite,
tree.traverseInOrder()
does not appear in your posted code. Without accurate code and the full error message, including the full traceback, we can't debug your problem.
I also tried adding that line at the bottom of your code, and finally got the error message you cite. The run-time system is correct (no surprise): there is no traverseInOrder method for class BalancedTree. That name does exist for class Node, but that's not what you called. You've confused your two classes. Sort this out, and the code may well work.
I'm trying to simplify one of my homework problems and make the code a little better. What I'm working with is a binary search tree. Right now I have a function in my Tree() class that finds all the elements and puts them into a list.
tree = Tree()
#insert a bunch of items into tree
then I use my makeList() function to take all the nodes from the tree and puts them in a list.
To call the makeList() function, I do tree.makeList(tree.root). To me this seems a little repetitive. I'm already calling the tree object with tree.so the tree.root is just a waste of a little typing.
Right now the makeList function is:
def makeList(self, aNode):
if aNode is None:
return []
return [aNode.data] + self.makeList(aNode.lChild) + self.makeList(aNode.rChild)
I would like to make the aNode input a default parameter such as aNode = self.root (which does not work) that way I could run the function with this, tree.makeList().
First question is, why doesn't that work?
Second question is, is there a way that it can work? As you can see the makeList() function is recursive so I cannot define anything at the beginning of the function or I get an infinite loop.
EDIT
Here is all the code as requested:
class Node(object):
def __init__(self, data):
self.data = data
self.lChild = None
self.rChild = None
class Tree(object):
def __init__(self):
self.root = None
def __str__(self):
current = self.root
def isEmpty(self):
if self.root == None:
return True
else:
return False
def insert (self, item):
newNode = Node (item)
current = self.root
parent = self.root
if self.root == None:
self.root = newNode
else:
while current != None:
parent = current
if item < current.data:
current = current.lChild
else:
current = current.rChild
if item < parent.data:
parent.lChild = newNode
else:
parent.rChild = newNode
def inOrder(self, aNode):
if aNode != None:
self.inOrder(aNode.lChild)
print aNode.data
self.inOrder(aNode.rChild)
def makeList(self, aNode):
if aNode is None:
return []
return [aNode.data] + self.makeList(aNode.lChild) + self.makeList(aNode.rChild)
def isSimilar(self, n, m):
nList = self.makeList(n.root)
mList = self.makeList(m.root)
print mList == nList
larsmans answered your first question
For your second question, can you simply look before you leap to avoid recursion?
def makeList(self, aNode=None):
if aNode is None:
aNode = self.root
treeaslist = [aNode.data]
if aNode.lChild:
treeaslist.extend(self.makeList(aNode.lChild))
if aNode.rChild:
treeaslist.extend(self.makeList(aNode.rChild))
return treeaslist
It doesn't work because default arguments are evaluated at function definition time, not at call time:
def f(lst = []):
lst.append(1)
return lst
print(f()) # prints [1]
print(f()) # prints [1, 1]
The common strategy is to use a None default parameter. If None is a valid value, use a singleton sentinel:
NOTHING = object()
def f(arg = NOTHING):
if arg is NOTHING:
# no argument
# etc.
If you want to treat None as a valid argument, you could use a **kwarg parameter.
def function(arg1, arg2, **kwargs):
kwargs.setdefault('arg3', default)
arg3 = kwargs['arg3']
# Continue with function
function("amazing", "fantastic") # uses default
function("foo", "bar", arg3=None) # Not default, but None
function("hello", "world", arg3="!!!")
I have also seen ... or some other singleton be used like this.
def function(arg1, arg2=...):
if arg2 is ...:
arg2 = default
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.