Problem with the print of a python tree data structure [duplicate] - python

This question already has answers here:
Python prints None
(2 answers)
Closed 3 years ago.
When I run this program in m_tree.print_values(root) appears the data and 3 times None to any Node present in this tree. I don't understand why appears these None because the function should print only the value.
class Tree:
def __init__(self, root=None):
self.root = root
def print_values(self, root):
if root != None:
print(root.data)
print(self.print_values(root.left))
print(self.print_values(root.right))
#Define other tree operations that you want to perform here
class Node:
def __init__(self, data=0, left=None, right=None):
self.data = data
self.left=left
self.right=right
#Create a root node
root = Node(0)
#Create a tree with the root node
m_tree = Tree(root)
#Add a left and right node to the root
left_node = Node(3)
right_node = Node(4)
root.left = left_node
root.right = right_node
m_tree.print_values(root)

print_values has no return statement, so it returns None by default. So if you call print_values and print the result, it will print None.
Change your recursive calls so that they call print_values without printing the result.
def print_values(self, root):
if root != None:
print(root.data)
self.print_values(root.left)
self.print_values(root.right)

You're calling
print(self.print_values(root.left))
The self.print_values function prints the values, and then returns None. So first you get the value printed by print_values, and then you print(None) because that's what self.print_values returned.

Related

Python class basics functions not working

Why the binary tree is not getting inverted in this class?
class Node:
def __init__(self, data):
self.left = None
self.right= None
self.data = data
def show_tree (self):
if self.left:
self.left.show_tree()
print(self.data)
if self.right:
self.right.show_tree()
class Operation:
def invertTree(self,root):
if root:
root.left, root.right==root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
root = Node(10)
b=Node(15)
c=Node(19)
d=Node(5)
e=Node(59)
d.left=e
d.right=c
root.left=b
root.right=d
root.show_tree()
oper=Operation()
inver =oper.invertTree(root)
inver.show_tree()
The other class is returning the inverted root so finally it should return the inverted root to inver but when I display it, it shows same tree
root.left, root.right==root.right, root.left compares the values and returns a boolean, if you want to assign you need to use a single = operator

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.

Yield all root-to-leaf branches of a Binary Tree

Sorry if this is a common question but I haven't found an appropriate answer for my particular problem. I'm trying to implement a walk method that walks a binary tree from its root node to each of its leaf nodes, yielding the root-to-leaf path whenever I get to a leaf node. For example, walking the binary tree represented by:
__a__
/ \
b d
/ \ / \
- c - -
Would yield:
['a', 'b', 'c']
['a', 'd']
My idea is that BinaryTree.walk calls Node.traverse on the root node, which in turn calls the traversemethod of each child node recursively. BinaryTree.walk also creates an empty list which is passed around with each traverse call, appending the data of each node, yielding the list once a leaf node is reached and popping each element out of the list after visiting each node.
At some point, something is going wrong though. This is my code:
class Node:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
def __repr__(self):
return f"{self.__class__.__name__}({self.data})"
#property
def children(self):
return self.left, self.right
def traverse(self, branch):
print('ON NODE:', self)
branch.append(self.data)
if self.left is None and self.right is None:
yield branch
else:
for child in self.children:
if child is not None:
print('ENTERING CHILD:', child)
child.traverse(branch=branch)
print('EXITING CHILD:', child)
branch.pop()
class BinaryTree:
def __init__(self, root=Node()):
if not isinstance(root, Node):
raise ValueError(f"Tree root must be Node, not {type(root)}")
self.root = root
def __repr__(self):
return f"{self.__class__.__name__}({self.root})"
def walk(self):
node = self.root
branch = []
yield from node.traverse(branch=branch)
if __name__ == '__main__':
# create root node
n0 = Node('A')
# create binary tree with root node
tree = BinaryTree(root=n0)
# create others nodes
n1 = Node(data='B')
n2 = Node(data='C')
n3 = Node(data='D')
# connect nodes
n0.left = n1
n0.right = n3
n1.right = n2
# walk tree and yield branches
for branch in tree.walk():
print(branch)
Expected output:
ON NODE: Node(A)
ENTERING CHILD: Node(B)
ON NODE: Node(B)
ENTERING CHILD: Node(C)
ON NODE: Node(C)
['A', 'B', 'C'] # yielded branch
EXITING CHILD: Node(C)
EXITING CHILD: Node(B)
ENTERING CHILD: Node(D)
ON NODE: Node(D)
['A', 'D'] # yielded branch
EXITING CHILD: Node(D)
Actual output:
ON NODE: Node(A)
ENTERING CHILD: Node(B)
EXITING CHILD: Node(B)
ENTERING CHILD: Node(D)
EXITING CHILD: Node(D)
IndexError: pop from empty list
I understand I'm doing something wrong with the list since it's trying to pop when it's empty, but I can't understand how it got to that. It should call pop once for each append call.
Also I can't figure out why the nodes are being entered and exited, but the ON NODE: message is not being printed... It's like my code just skips the child.traverse(branch=branch) line somehow?
Can anyone help me understand where I'm messing this up?
Thanks in advance for your help!
Here's a modified variant of your code.
code.py:
#!/usr/bin/env python3
import sys
class Node:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
def __repr__(self):
return f"{self.__class__.__name__}({self.data})"
#property
def children(self):
if self.left:
yield self.left
if self.right:
yield self.right
#property
def is_leaf(self):
return self.left is None and self.right is None
def traverse_preord(self, accumulator=list()):
print(" On node:", self)
accumulator.append(self.data)
if self.is_leaf:
yield accumulator
else:
for child in self.children:
print(" Entering child:", child)
yield from child.traverse_preord(accumulator=accumulator)
accumulator.pop()
print(" Exiting child:", child)
def main():
root = Node(data="A",
left=Node(data="B",
right=Node(data="C")
),
right=Node(data="D",
#left=Node(data="E"),
#right=Node(data="F"),
)
)
for path in root.traverse_preord():
print("Found path:", path)
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
Notes:
I refactored the code a bit (simplified, changed some identifier names, texts and other insignificant changes)
children property:
None for a node's left or right attribute, means that the node has no child, so no point including it in the returned result
Since the question is involving yield, I turned it into a generator (instead of returning a tuple or list, ...). As a consequence, I had to add is_leaf, since a generator doesn't evaluate to False (even if empty)
Output:
[cfati#CFATI-5510-0:e:\Work\Dev\StackOverflow\q055424449]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
On node: Node(A)
Entering child: Node(B)
On node: Node(B)
Entering child: Node(C)
On node: Node(C)
Found path: ['A', 'B', 'C']
Exiting child: Node(C)
Exiting child: Node(B)
Entering child: Node(D)
On node: Node(D)
Found path: ['A', 'D']
Exiting child: Node(D)
What is wrong with your code?
It's the traverse recurring call (child.traverse(branch=branch)). It created a generator, but since that wasn't used (iterated on) anywhere, the function didn't actually called itself, resulting in the attempt of removing more elements than added (only 1: which is the root node). So, it turns out that you were almost there. All you have to do, is adding a yield from in front of it :). More details on [Python]: PEP 380 -- Syntax for Delegating to a Subgenerator.
There is a great answer here
Copying their Python example:
"""
Python program to print all path from root to
leaf in a binary tree
"""
# binary tree node contains data field ,
# left and right pointer
class Node:
# constructor to create tree node
def __init__(self, data):
self.data = data
self.left = None
self.right = None
# function to print all path from root
# to leaf in binary tree
def printPaths(root):
# list to store path
path = []
printPathsRec(root, path, 0)
# Helper function to print path from root
# to leaf in binary tree
def printPathsRec(root, path, pathLen):
# Base condition - if binary tree is
# empty return
if root is None:
return
# add current root's data into
# path_ar list
# if length of list is gre
if(len(path) > pathLen):
path[pathLen] = root.data
else:
path.append(root.data)
# increment pathLen by 1
pathLen = pathLen + 1
if root.left is None and root.right is None:
# leaf node then print the list
printArray(path, pathLen)
else:
# try for left and right subtree
printPathsRec(root.left, path, pathLen)
printPathsRec(root.right, path, pathLen)
# Helper function to print list in which
# root-to-leaf path is stored
def printArray(ints, len):
for i in ints[0 : len]:
print(i," ",end="")
print()
# Driver program to test above function
"""
Constructed binary tree is
10
/ \
8 2
/ \ /
3 5 2
"""
root = Node(10)
root.left = Node(8)
root.right = Node(2)
root.left.left = Node(3)
root.left.right = Node(5)
root.right.left = Node(2)
printPaths(root)
# This code has been contributed by Shweta Singh.
Gives:
10 8 3
10 8 5
10 2 2
You can give it letters like you have too:
root = Node("A")
root.left = Node("B")
root.right = Node("D")
root.left.right = Node("C")
printPaths(root)
Gives:
A B C
A D

Recursive function failing while inorder traverse in tree ds

I am trying to implement tree data structure but I am stuck and having trouble understanding how to create a recursive function for inorder traversal for my binary tree.
This is what i have done so far:
class Node:
def __init__(self, node):
self.node = node
self.left = None
self.right= None
def inorder_traversal(self):
if self.node != None:
return inorder_traversal(self.node.left)
return self.node
return inorder_traversal(self.node.right)
I don't seem to understand what's wrong.
Test inputs:
root = Node(1)
root.left = Node(3)
root.right = Node(4)
Error:
File "trees-implementation.py", line 23, in inorder_traversal
return inorder_traversal(self.node.left)
NameError: name 'inorder_traversal' is not defined
It looks like you've defined your traversal functions with respect to the Node. Does this make sense to you?
You should define a traversal with respect to the Tree, not the Node. The Node does not know it belongs to the tree, it's a dumb object.
Define the node.
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right= None
Define the tree. Also define your traversal methods here.
class Tree:
def __init__(self, root=None):
self.root = root
def inorder_traversal(self):
def _inorder(root):
if root != None:
yield from _inorder(root.left)
yield root.value
yield from _inorder(root.right)
return list(_inorder(self.root))
Initialise the tree with a root, and then traverse it.
tree = Tree(root)
tree.inorder_traversal()
[3, 1, 4]
First, could you check if when you have your object, you don't put a parameter in
root = Node()
Then are you sure you can have several returns in your inorder_traversal() function ?
Finally, the function is in the class Node() so if you call it try to add self.yourfunction

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

Categories

Resources