Creating a List from a Binary Search Tree - python

I'm trying to make a list of all items in a binary search tree. I understand the recursion but I don't know how to make it return each value and then append it into a list. I want to create a function called makeList() that will return a list of all the items in my tree. All the functions in my programs work except the makeList() function and are included to make sure everyone understands the basic structure of how I set up my tree.
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:
pass
if aNode != None:
self.inOrder(aNode.lChild)
print aNode.data
self.inOrder(aNode.rChild)
def makeList(self, aNode):
a = []
self.inOrder(aNode)
a += [aNode.data]
print a
n = Tree()
for i in [4,7,2,9,1]:
n.insert(i)
n.makeList(n.root)
Looking at my makeList() function I can see why it doesn't work but I don't know how to make it work.
EDIT
Ok, I got it! And I even got two answers which are:
def makeList(self, aNode, a = []):
if aNode != None:
self.makeList(aNode.lChild, a)
a += [aNode.data]
self.makeList(aNode.rChild, a)
return a
and
def makeList2(self, aNode):
if aNode is None:
return []
return self.makeList2(aNode.lChild) + [aNode.data] + self.makeList2(aNode.rChild)
And looking back I can see that I do not understand recursion very well so it's time to hit the books! Anyone have any good resources on recursion?
Another question, so say I call my makeList() function. When Python goes through makeList(), when it gets to the self.makeList(aNode.lChild, a) does it begin running the function again while it's still finishing up the makeList() function or does everything stop and it just starts over with it's new aNode?
I hope that makes sense.

You're so close! makeList can be pretty simple:
def makeList(self, aNode):
if aNode is None:
# Stop recursing here
return []
return self.makeList(aNode.lChild) + [aNode.data] + self.makeList(aNode.rChild)
Basically, make sure you're not trying to recurse past empty nodes. Then return the list of the left tree, the current node, and the list of the right tree.

inOrder prints things but does not return anything, so it's useless for building a list. You need a way to return each node in order. This may be something that your class hasn't covered yet, but check out the yield command.

The basic idea is something like this:
def makeList(self):
return self.lChild.makeList() + [self.data] + self.rChild.makeList()
See how it is essentially the same thing as inOrder?
You have a different structure in your program that makes it a bit harder to implement, but the basic idea is the same.

Related

I do not understand why my method for adding nodes into binary tree does not work?

I am trying to learn Python better, and I know one can easily find the solution for adding items into BTS, but I do not understand why my implementation of add method does not work? Can someone please help?
class Node:
def __init__(self, data, left = None, right = None):
self.data = data
self.left = left
self.right = right
class Tree:
def __init__(self):
self.root = None
def add(self, data):
self.root = self._add(data,self.root)
# if I change this line with just "self._add(data,self.root)" it would not iterate tree at all..
def _add(self,data,root):
if root is None:
root = Node(data)
return root
elif data <= root.data:
root.left = self._add(data, root.left)
else:
root.right = self._add(data,root.right)
def in_order(self):
if self.root is not None:
self._in_order(self.root)
def _in_order(self, root):
if root is not None:
self._in_order(root.left)
print(str(root.data) + ' ')
self._in_order(root.right)
I am printing in classical in-order fashion. My question is after inserting a few numbers, why do I still get the result for a root node to be None ?
This kind of implementation worked for me before in C or in Java, but in Python it does not work.
EDIT: I added my in-order method.
I have also tried this but it also does not work:
def add(self,data):
if self.root == None:
self.root = Node(data)
elif data <= self.root.data:
self.root.left = self.add(self.root.left,data)
else:
self.root.right = self.add(self.root.right,data)
I get a TypeError: add() takes 2 positional arguments but 3 were given
My question is after inserting a few numbers, why do I still get the result for a root node to be None ?
With this following line, you're setting self.root to be the return value of the method _add. Since this method has no return statement, it returns None by default.
self.root = self._add(data,self.root)
I get a TypeError: add() takes 2 positional arguments but 3 were given
In this case, you're calling self.add(self.root.left,data) with three parameters: the implicit self reference, self.root.left and data.
However, the method declaration receives only the parameters self and data.

Why creating an instance of a class within a class method changes the 'self' argument?

I'm writing a linked list in Python and I've come across an issue which is really troublesome and terrible for debugging and I feel like I'm missing something about Python. I'm supposed to create a singly linked list with some basic functionalities. One of them is the take() function, which is meant to create a new list of n first elements of the original list.
However, creating a new instance of the LinkedList class seems to change the .self parameter and the variable node is modified, as the attribute .next is turned to None. In result, when creating a list and then trying to make a new one out of the n elements of it, the program runs indefinitely, but no matter which part I look at, I cannot find the loop or the reason behind it.
class LinkedList:
def __init__(self, head=None):
self.head = head
def is_empty(self):
if self.head == None:
return True
else:
return False
def add_last(self, node):
if self.is_empty():
self.head = node
return
nextEl = self.head
while True:
if nextEl.next is None:
nextEl.next = node
return
nextEl = nextEl.next
def take(self, n):
node = self.head
newHead = self.head
newHead.next = None
newList = LinkedList(newHead)
count = 0
while count < n:
newList.add_last(node.next)
node = node.next
count += 1
return newList
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
Thank you for all your help.
In the take() function the line
newHead.next = None
modifies a node of the linked list, breaking this list. You can fix this as follows:
def take(self, n):
node = self.head
newHead = Node(self.head.data)
newList = LinkedList(newHead)
count = 0
while count < n:
newList.add_last(node.next)
node = node.next
count += 1
return newList
This, however, still will not work correctly since there is a problem with the add_last() function too. This function, I think, is supposed to add a node as the last element of a linked list, but since you do not modify the next attribute of the node, you actually append a whole linked list starting with that node. This can be fixed in the following way:
def add_last(self, node):
if self.is_empty():
self.head = node
return
nextEl = self.head
while True:
if nextEl.next is None:
nextEl.next = Node(node.data)
return
nextEl = nextEl.next
There are more issues. For example, take(sefl, n) will actually create a list of n+1 elements and will throw an exception if it is applied to a linked list that does not have that many elements.

Python inheriting base class variable [duplicate]

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

Python BST not working

I'm new to Python thus the question,this is the implementation of my my BST
class BST(object):
def __init__(self):
self.root = None
self.size = 0
def add(self, item):
return self.addHelper(item, self.root)
def addHelper(self, item, root):
if root is None:
root = Node(item)
return root
if item < root.data:
root.left = self.addHelper(item, root.left)
else:
root.right = self.addHelper(item, root.right)
This is the Node object
class Node(object):
def __init__(self, data):
self.data = data
self.left = None
self.right = None
This is my implmentation of str
def __str__(self):
self.levelByLevel(self.root)
return "Complete"
def levelByLevel(self, root):
delim = Node(sys.maxsize)
queue = deque()
queue.append(root)
queue.append(delim)
while queue:
temp = queue.popleft()
if temp == delim and len(queue) > 0:
queue.append(delim)
print()
else:
print(temp.data, " ")
if temp.left:
queue.append(temp.left)
if temp.right:
queue.append(temp.right)
This is my calling client,
def main():
bst = BST()
bst.root = bst.add(12)
bst.root = bst.add(15)
bst.root = bst.add(9)
bst.levelByLevel(bst.root)
if __name__ == '__main__':
main()
Instead of the expected output of printing the BST level by level I get the following output,
9
9223372036854775807
When I look in the debugger it seems that the every time the add method is called it starts with root as None and then returns the last number as root. I'm not sure why this is happening.
Any help appreciated.
If the root argument of your addHelper is None, you set it to a newly-created Node object and return it. If it is not, then you modify the argument but return nothing, so you end up setting bst.root to None again. Try the following with your code above — it should help your understanding of what your code is doing.
bst = BST()
bst.root = bst.add(12)
try:
print(bst.root.data)
except AttributeError:
print('root is None')
# => 12
# `bst.addHelper(12, self.root)` returned `Node(12)`,
# which `bst.add` returned too, so now `bst.root`
# is `Node(12)`
bst.root = bst.add(15)
try:
print(bst.root.data)
except AttributeError:
print('root is None')
# => root is None
# `bst.addHelper(15, self.root)` returned `None`,
# which `bst.add` returned too, so now `bst.root`
# is `None`.
bst.root = bst.add(9)
try:
print(bst.root.data)
except AttributeError:
print('root is None')
# => 9
# `bst.addHelper(9, self.root)` returned `Node(9)`,
# which `bst.add` returned too, so now `bst.root`
# is `Node(9)`
So you should do two things:
make you addHelper always return its last argument — after the appropriate modifications —, and
have your add function take care of assigning the result to self.root (do not leave it for the class user to do).
Here is the code:
def add(self, item):
self.root = self.addHelper(item, self.root)
self.size += 1 # Otherwise what good is `self.size`?
def addHelper(self, item, node):
if node is None:
node = Node(item)
elif item < node.data:
node.left = self.addHelper(item, node.left)
else:
node.right = self.addHelper(item, node.right)
return node
Notice that I changed the name of the last argument in addHelper to node for clarity (there already is something called root: that of the tree!).
You can now write your main function as follows:
def main():
bst = BST()
bst.add(12)
bst.add(15)
bst.add(9)
bst.levelByLevel(bst.root)
(which is exactly what #AaronTaggart suggests — but you need the modifications in add and addHelper). Its output is:
12
9
15
9223372036854775807
The above gets you to a working binary search tree. A few notes:
I would further modify your levelByLevel to avoid printing that last value, as well as not taking any arguments (besides self, of course) — it should always print from the root of the tree.
bst.add(None) will raise an error. You can guard against it by changing your add method. One possibility is
def add(self, item):
try:
self.root = self.addHelper(item, self.root)
self.size += 1
except TypeError:
pass
Another option (faster, since it refuses to go on processing item if it is None) is
def add(self, item):
if item is not None:
self.root = self.addHelper(item, self.root)
self.size += 1
From the point of view of design, I would expect selecting a node from a binary search tree would give me the subtree below it. In a way it does (the node contains references to all other nodes below), but still: Node and BST objects are different things. You may want to think about a way of unifying the two (this is the point in #YairTwito's answer).
One last thing: in Python, the convention for naming things is to have words in lower case and separated by underscores, not the camelCasing you are using — so add_helper instead of addHelper. I would further add an underscore at the beginning to signal that it is not meant for public use — so _add_helper, or simply _add.
Based on the following, you can see that bst.root in None after the second call to add():
>>> bst.root = bst.add(12)
>>> bst.root
<__main__.Node object at 0x7f9aaa29cfd0>
>>> bst.root = bst.add(15)
>>> type(bst.root)
<type 'NoneType'>
Your addHelper isn't returning the root node. Try this:
def addHelper(self, item, root):
if root is None:
root = Node(item)
return root
if item < root.data:
root.left = self.addHelper(item, root.left)
else:
root.right = self.addHelper(item, root.right)
return root
And then it works as expected:
>>> bst.root = bst.add(12)
>>> bst.root = bst.add(15)
>>> bst.levelByLevel(bst.root)
(12, ' ')
()
(15, ' ')
(9223372036854775807, ' ')
>>> bst.root = bst.add(9)
>>> bst.levelByLevel(bst.root)
(12, ' ')
()
(9, ' ')
(15, ' ')
(9223372036854775807, ' ')
You're using the BST object basically only to hold a root Node and the add function doesn't really operate on the BST object so it's better to have only one class (BtsNode) and implement the add there. Try that and you'll see that the add function would be much simpler.
And, in general, when a member function doesn't use self it shouldn't be a member function (like addHelper), i.e., it shouldn't have self as a parameter (if you'd like I can show you how to write the BtsNode class).
I tried writing a class that uses your idea of how to implement the BST.
class BstNode:
def __init__(self):
self.left = None
self.right = None
self.data = None
def add(self,item):
if not self.data:
self.data = item
elif item >= self.data:
if not self.right:
self.right = BstNode()
self.right.add(item)
else:
if not self.left:
self.left = BstNode()
self.left.add(item)
That way you can create a BST the following way:
bst = BstNode()
bst.add(13)
bst.add(10)
bst.add(20)
The difference is that now the add function actually operates on the object without any need for the user to do anything. The function changes the state of the object by itself.
In general a function should do only what it's expected to do. The add function is expected to add an item to the tree so it shouldn't return the root. The fact that you had to write bst.root = bst.add() each time should signal that there's some fault in your design.
Your add method probably shouldn't return a value. And you most certainly shouldn't assign the root of the tree to what the add method returns.
Try changing your main code to something like this:
def main():
bst = BST()
bst.add(12)
bst.add(15)
bst.add(9)
bst.levelByLevel(bst.root)
if __name__ == '__main__':
main()

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.

Categories

Resources