how to update class attribute value from method - python

I am trying to implement a Binary Tree using a queue. I am having an issue to set a new value for self.root who is initially set in class BinaryTree as None using the method InsertNode(self,data). I try to return self.root with it's new value but the value remains the same (None) Any Idea?
class BinaryTree:
def __init__(self):
self.root = None
def InsertNode(self, data):
newNode = Node(data)
newNode = newNode.data
print('self.root =', self.root)
print('new node', newNode)
if self.root == None:
self.root = newNode
print('self.root =', self.root)
else:
print('else won')
queue = []
# print(queue)
queue.append(self.root)
while True:
node = queue.pop(0)
if node.left != None and node.right != None:
queue.append(node.left)
queue.append(node.right)
else:
if node.left == None:
node.left = newNode
queue.append(node.left)
else:
if node.right == None:
node.right = newNode
queue.append(node.right)
break
return self.root

Some issues:
Right after creating a new node and assigning its reference to newNode, your code assigns the data of that node to newNode. At that point you lose the reference to your node, and it is forever lost. Moreover, while self.root is supposed to be node reference, you then continue to assign the data value to self.root. In short, don't do newNode = newNode.data
The while loop can only make one iteration, because it has an unconditional break. That break should be conditional: it should be indented one level more so it is placed in the else block.
With those two fixes, your code will work (provided the other code you have, like for class Node is error-free).
Not a problem, but you should also look at these points:
At the place where you have if node.right == None, this condition is always going to be true, given that it was already known that not both node.left and node.right are None, and also that node.left is None. So the only remaining possibility for node.right is that it is None.
Once you have assigned newNode to where it belongs, there is no need any more to append to the queue.
When using a list as a FIFO queue, it is better to use a deque and its popleft method.
In Python, names of methods are commonly written with a first lowercase letter. Either name your method insertNode or why not just insert?
As node.left is a node reference or None, you can just test for node.left instead of node.left != None. Similarly for node.right
Instead of testing for the end-condition inside the while True loop, you could change the order of your code a bit and make it a while node.left and node.right loop. Then the assignment of newNode to the right attribute can happen after the loop has finished.
The insert method does not need to return the root node. The reference of the root node is maintained in an attribute of the instance. The outside caller should in fact have no need to ever access node references. I would suggest not to return the root node.
Here is the resulting code. I added __repr__ implementations so the tree can be printed with some basic indentation format:
from collections import deque
class Node:
def __init__(self, data):
self.data = data
self.left = self.right = None
def __repr__(self, tab=""):
return ((self.right.__repr__(" " + tab) if self.right else "")
+ tab + repr(self.data) + "\n"
+ (self.left.__repr__(" " + tab) if self.left else ""))
class BinaryTree:
def __init__(self):
self.root = None
def insert(self, data):
newNode = Node(data)
node = self.root
if not node:
self.root = newNode
else:
queue = deque()
while node.left and node.right:
queue.append(node.left)
queue.append(node.right)
node = queue.popleft()
if node.left:
node.right = newNode
else:
node.left = newNode
def __repr__(self):
return repr(self.root).rstrip() if self.root else "<Empty>"
# Example run
tree = BinaryTree()
print(tree)
for i in range(1, 13):
tree.insert(i)
print(tree)

Related

How to fix 'NoneType' has no attribute 'key', when trying to compare a key value to a string

I am writing a program where the user inputs a postfix expression and it outputs the answer. Current I am stuck when Using my 'evaluate' function within my for loop.
Inside my For loop Main.py:
else:
# Debug Code
print('{}: Else'.format(i))
print('{}: Length'.format(len(stack)))
Node.right = stack.pop()
Node.left = stack.pop()
Node = TreeNode(str(i))
stack.push(str(i))
# Debug Code
print('{}: Right Key'.format(Node.right))
print('{}: Left Key'.format(Node.left))
print('{}: Node Key'.format(Node.key))
print('{}: Node Key Type'.format(type(Node.key)))
Node = evaluate(Node)
stack.push(int(Node))
I am getting the error below:
Traceback (most recent call last):
File "c:\Users\dpr48\main.py", line 49, in <module>
Node = evaluate(Node)
File "c:\Users\dpr48\main.py", line 10, in evaluate
return evaluate(node.left) + evaluate(node.right)
File "c:\Users\dpr48\main.py", line 9, in evaluate
if node.key == '+':
AttributeError: 'NoneType' object has no attribute 'key'
So my question is why is it not using the 'TreeNode' class to get the key value? As well as the line of code that should define the 'Node.left' as the 'stack.pop()' value and 'Node.right' as the 'stack.pop()' value ends up not changing either of them and leaves them as None, as found in the 'Debug Code' that I have implemented to see what the program is doing interenally.
Provided each class used below:
Main.py
from Stack import Stack
from TreeNode import TreeNode
def evaluate(node):
if node.key == '+':
return evaluate(node.left) + evaluate(node.right)
elif node.key == '-':
return evaluate(node.left) - evaluate(node.right)
elif node.key == '*':
return evaluate(node.left) * evaluate(node.right)
elif node.key == '/':
return evaluate(node.left) / evaluate(node.right)
else:
return node.key
stack = Stack()
exp = "23+"
list = [*exp]
for i in list:
if i.isdigit() is True:
# Debug Code
print('{}: True'.format(i))
Node = TreeNode(int(i))
stack.push(int(i))
else:
# Debug Code
print('{}: Else'.format(i))
print('{}: Length'.format(len(stack)))
Node.right = stack.pop()
Node.left = stack.pop()
Node = TreeNode(str(i))
stack.push(str(i))
# Debug Code
print('{}: Right Key'.format(Node.right))
print('{}: Left Key'.format(Node.left))
print('{}: Node Key'.format(Node.key))
print('{}: Node Key Type'.format(type(Node.key)))
Node = evaluate(Node)
stack.push(int(Node))
print(evaluate(stack.node))
Stack.py
from Node import Node
from LinkedList import LinkedList
class Stack:
def __init__(self):
self.list = LinkedList()
def push(self, new_item):
# Create a new node to hold the item
new_node = Node(new_item)
# Insert the node as the list head (top of stack)
self.list.prepend(new_node)
def pop(self):
# Copy data from list's head node (stack's top node)
popped_item = self.list.head.data
# Remove list head
self.list.remove_after(None)
# Return the popped item
return popped_item
def __len__(self):
node = self.list.head # Start at head of stack to count until stack returns Null
count = 0
while node != None:
node = node.next
count+=1
return count # Returning length of stack
LinkedList.py
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
def append(self, new_node):
if self.head == None:
self.head = new_node
self.tail = new_node
else:
self.tail.next = new_node
self.tail = new_node
def prepend(self, new_node):
if self.head == None:
self.head = new_node
self.tail = new_node
else:
new_node.next = self.head
self.head = new_node
def insert_after(self, current_node, new_node):
if self.head == None:
self.head = new_node
self.tail = new_node
elif current_node is self.tail:
self.tail.next = new_node
self.tail = new_node
else:
new_node.next = current_node.next
current_node.next = new_node
def remove_after(self, current_node):
# Special case, remove head
if (current_node == None) and (self.head != None):
succeeding_node = self.head.next
self.head = succeeding_node
if succeeding_node == None: # Remove last item
self.tail = None
elif current_node.next != None:
succeeding_node = current_node.next.next
current_node.next = succeeding_node
if succeeding_node == None: # Remove tail
self.tail = current_node
Node.py
class Node:
def __init__(self, initial_data):
self.data = initial_data
self.next = None
TreeNode.py
class TreeNode:
# Constructor assigns the given key, with left and right
# children assigned with None.
def __init__(self, key):
self.key = key
self.left = None
self.right = None
There are several issues:
Node is the name of a class, yet you use the same name for a TreeNode instance, shadowing the class name. This is not the main problem, but certainly not advised. Related: Don't use PascalCase for instances, but camelCase. So node, not Node.
You assign to Node.right when you have not yet defined Node yet, which happens later with Node = TreeNode(str(i)). You should first assign to Node (well, better node) and only then assign to its attributes.
With Node.right = stack.pop() you clearly expect the stack to contain TreeNode instances, but with stack.push(str(i)) you push strings. That will lead to the problems you describe. The stack should not be populated with strings, but with TreeNode objects.
At the end of the else block you call evaluate, and then push that result value to the stack. This is wrong and should be removed. The evaluation should only happen when you have completed the tree, and it should not involve the stack. The stack has a role in building the tree, not in evaluating it.
The final print line makes an access to stack.node, but stack has no node attribute. You'll want to pop the top item from the stack, which (if the input syntax was correct) should only have 1 node left on it, representing the root of the tree.
Not a problem, but i is guaranteed to be a string (with length 1), so there is no need to call str on it.
Here is the corrected code:
for i in list:
if i.isdigit() is True:
node = TreeNode(int(i)) # lowercase name
stack.push(node) # don't push string, but object
else:
node = TreeNode(i) # First create the node
node.right = stack.pop() # Then assign to its attributes
node.left = stack.pop()
stack.push(node) # don't push string
# Don't evaluate here, nor push anything else to the stack
print(evaluate(stack.pop()))

Deletion in BST (python) | unexpected additional deletions?

def delete(node, key):
if not node: return None
# Wrong node, search correct child
if key < node.data:
delete(node.left, key)
elif key > node.data:
delete(node.right, key)
# Correct node found
else:
#1. node has no children
if not (node.left and node.right): return None
#2. node has only left child
if node.left and not node.right: return node.left
#3. node has only right child
if not node.left and node.right: return node.right
#4. node has both left & right children
## Need to replace current value with next biggest value
## So go right once then all left to end
## Once this value is found, assign to appropriate position
## Then remove this val from its previous position
temp = node.right
while temp.left: temp = temp.left
node.data = temp.data
node.right = delete(node.right, temp.data)
t = BinaryTree([100, 50, 200, 25, 75, 350])
delete(t.root, 100)
I think that this BST deletion code mostly works, but it's a little buggy. If I delete the root node, 100, then 350 will be missing, following, given the BST, t = BinaryTree([100, 50, 200, 25, 75, 350]).
What is going on here? I'm not sure why 350 has been deleted in the process. I'm wondering if it's related to how I replace the node value upon successful deletion.
Optional but possibly helpful context
class BinaryTreeNode:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
class BinaryTree:
def __init__(self, *args):
if len(args) < 1:
self.root = None
elif isinstance(args[0], int):
self.root = BinaryTreeNode(args[0])
else:
self.root = None
for x in args[0]:
self.insert(x)
def insert(self, node_data):
new_node = BinaryTreeNode(node_data)
if not self.root:
self.root = new_node
else:
# root has no parent, so assign none for 1st iteration
parent = None
temp_pointer = self.root
while temp_pointer:
# update parent
parent = temp_pointer
#update temp_pointer to left or right child
if node_data <= temp_pointer.data:
temp_pointer = temp_pointer.left
else:
temp_pointer = temp_pointer.right
# eventually, temp_pointer will point to None, exiting while loop
# assign to left or right child as appropriate
if node_data <= parent.data:
parent.left = new_node
else:
parent.right = new_node
There are a few issues:
As delete is designed to return a node reference or None, you should make sure not to ignore that returned reference. You did it right near the end of your function (node.right = delete(node.right, temp.data)), but elsewhere delete is called without regards of the returned reference. So:
The initial call in the main program should look like this:
t.root = delete(t.root, 100)
This will ensure that the root attribute is set to None when the last node has been deleted from the tree.
The recursive call in the first if block should be:
node.left = delete(node.left, key)
And similarly in the second block:
node.right = delete(node.right, key)
The function delete should always return a node reference after a recursive call has been made, yet this is missing in many of your cases, so add at the very bottom of your function a kind of "catch all" and return the current reference you have:
return node
The condition for identifying a leaf node is wrong. The and should be a or:
if not (node.left or node.right): return None
The corrected code -- comments indicate changes:
def delete(node, key):
if not node: return None
if key < node.data:
node.left = delete(node.left, key) # assign back!
elif key > node.data:
node.right = delete(node.right, key) # assign back!
else:
if not (node.left or node.right): return None # condition corrected
if node.left and not node.right: return node.left
if not node.left and node.right: return node.right
temp = node.right
while temp.left: temp = temp.left
node.data = temp.data
node.right = delete(node.right, temp.data)
return node # always return a node when a recursive call was made
t = BinaryTree([100, 50, 200, 150, 175, 25, 75, 350])
t.root = delete(t.root, 350) # assign back!
Considerations
Not a problem in the algorithm, but it is a good habit to put the body of an if or while statement on the next line, indented
This function would better be a method on the BinaryTree class -- then the main program should not have to worry about getting/setting the root attribute -- and most of the function's (recursive) logic could be implemented as a method on the BinaryTreeNode class.

adding a node in Binary Search Tree

I am currently working on implementing a Binary search tree but I have a problem with creating the add_node() function. I tried to make it in a recursive way, but the result was weird.
For example- If I run add_node(1), add_node(5), and add_node(0), my tree has only 1, no 5 and 0.
I would be happy if you tell me what the problem is.
def add_node(self, value: int) -> None:
if self.root == None:
self.root = Node(value)
return
else:
return self.add_recursion(self.root, value)
def add_recursion(self, node: Node, value: int) -> None:
if node == None:
node = Node(value)
return
elif value < node.value:
return self.add_recursion(node.left, value)
else:
return self.add_recursion(node.right, value)
When a None value is passed into a function, it is passed by value, not by reference, since... there is no reference.
elif value < node.value:
return self.add_recursion(node.left, value)
else:
return self.add_recursion(node.right, value)
When node.left or node.right is None, a Node ends up being created but not attached to node.
So what you could do is handle the cases where they are None separately.
def add_recursion(self, node: Node, value: int) -> None:
if node == None:
node = Node(value)
elif value < node.value:
if node.left == None:
node.left = Node(value)
else:
self.add_recursion(node.left, value)
else:
if node.right == None:
node.right = Node(value)
else:
self.add_recursion(node.right, value)
While this is workable, it becomes quite ugly. Look at the answer by Locke to see a better way to structure your code.
Also, an additional tip for readability, avoid using return where they aren't necessary such as at the end of a flow.
The issue is how you handle if node.left or node.right does not exist. Since the arguments are copied for each call, setting the value of node in add_recursion has no effect on the tree.
def foo(val):
print("foo start:", val)
val = 5
print("foo end:", val)
bar = 3
foo(bar) # Value of bar is copied for call
print("after foo:", bar) # Prints bar is still 3
I think you might also be getting confused due to how the root node is handled. Here is some starter code on how can handle the initial call to add_recursive.
class Node:
def __init__(self, value: int):
self.value = value
self.left = None
self.right = None
def add_recursive(self, value: int):
# TODO: recursively add to left or right
# For example, here is how you could recursively make a linked list
if self.right is None:
self.right = Node(value)
else:
self.right.add_recursive(value)
class BinarySearchTree:
def __init__(self):
self.root = None
def add_node(self, value: int):
if self.root is None:
self.root = Node(value)
else:
# Call add_recursive on root instead of with root.
self.root.add_recursive(value)
tree = BinarySearchTree()
tree.add_node(1)
tree.add_node(5)
tree.add_node(0)

Python Binary Search Tree Delete Function

class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
class BST:
def __init__(self, root=None):
self.root = root
def remove(self, val):
if self.root == None:
return root
else:
self._remove(val, self.root)
def _remove(self, val, node):
if node == None:
return node # Item not found
if val < node.val:
self._remove(val, node.left)
elif val > node.val:
self._remove(val, node.right)
else:
# FOUND NODE TO REMOVE
if node.left != None and node.right != None: # IF TWO CHILDREN
node.val = self._find_min(node.right)
node.right = self._remove(node.val, node.right)
else: # ZERO OR ONE CHILD
if node.left == None: # COVERS ZERO CHILD CASE
node = node.right
elif node.right == None:
node = node.left
return node
Cannot figure out why this function will not delete some values. I debugged with print statements and can see that if I try to remove a value, the function will enter the else block and successfully remove a node with two children. However, when attempting to remove a node with one or zero children, the codes executes with no errors, but when I print the tree to view its contents the node is still there.
The node to be removed will have at least one None child, and it seems straightforward to set the node equal to its right (or left) child, which I assume sets the node to None.
I have some experience with Java, but fairly new to Python, and I sometimes run into trouble with the "self" protocol, but I don't think that is the case here.

Reference to object passed to function Python

To understand more about Python's objects and classes I wrote some code for a Binary Search Tree.
For the delete method when I try to change what the node is pointing to, python is creating a new variable instead of altering the node that is passed to the method. How would I be able to change what node is pointing to inside the method?
class Node:
def __init__(self):
self.data = None
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
self.length = 0
def insert(self, data):
if not self.root:
temp = Node()
temp.data = data
self.root = temp
else:
return self._insert(self.root, data)
def _insert(self, node, data):
if node.data <= data:
if not node.right:
temp = Node()
temp.data = data
node.right = temp
return
else:
self._insert(node.right, data)
else:
if not node.left:
temp = Node()
temp.data = data
node.left = temp
return
else:
self._insert(node.left, data)
def delete(self, data):
return self._delete(self.root, data)
def _delete(self, node, data):
if not node:
return False
elif node.data < data:
return self._delete(node.right, data)
elif node.data > data:
return self._delete(node.left, data)
else:
if not node.left and not node.right:
node = None
elif not node.left:
node = node.right
elif not node.right:
node = node.right
else:
temp = self.findmin(node.right)
#or find max of left
node.data = temp.data
self._delete(node.right, temp.data)
def findmin(self, node):
if not node:
return None
while node.left:
node = node.left
return node
This is not the python issue, this would be the same for every single language using pointers (and not c++ style references). During delete operation you should change pointer of the parent's node reference to its children. You have a structure of the form
parent
/ \
node ...
/ \
child1 child2
when removing node you have to change parent children to new ones, not try to assign new value to a "node" variable, which is not pointing anywhere. Thus you will end up with something like:
parent
/ \
child1 ...
/ \
child2 ...

Categories