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 ...
Related
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()))
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)
I have problem with infinite loop in getMinimal() method. It works in this way :
1)Take node,
2)If node has other node on the left - go to other one.
3)Repeat as far as node has sth on the left side
4)Return the minimal node.
But sometimes it works in infinite loop for example from 1000 to 400, then to 4 then..to 1000! I have no ide where I make mistake. I reviewed this code many times,every single "pointer" to parent/left/right node is okay! Please - help.
Algorithm works okay to "handwritten" trees - ~20nodes. I wanted to test it in better cases - 2500nodes,generated by random lib (from -10k to 10k).
import random
class Node:
def __init__(self, val):
self.val = val
self.parent = None
self.right = None
self.left = None
# Class of node.
def str(self):
return str(self.val)
class MyTree:
def __init__(self, node):
self.root = node
def insert(self, node):
current = self.root
a = True
while a:
if node.val > current.val:
if current.right is not None:
current = current.right
continue
else:
current.right = node
node.parent = current
a = False
if node.val <= current.val:
if current.left is not None:
current = current.left
continue
else:
current.left = node
node.parent = current
a = False
def search(self, node):
current = self.root
while node.val != current.val:
if node.val > current.val:
current = current.right
continue
elif node.val <= current.val:
current = current.left
continue
if node.val == current.val:
return current
else:
print("There is no such node!")
def delete(self, node):
if isinstance(node, (float, int)):
node = self.search(node)
if node is self.root:
self.__deleteRoot()
return
else:
if node.right is None and node.left is None:
self.__deleteNN(node)
return
if node.right is None and node.left is not None:
self.__deleteLN(node)
return
if node.right is not None and node.left is None:
self.__deleteNR(node)
return
if node.right is not None and node.left is not None:
self.__deleteLR(node)
return
def __deleteNN(self, node):
if node.parent.left is node:
node.parent.left = None
if node.parent.right is node:
node.parent.right = None
def __deleteLN(self, node):
parent = node.parent
son = node.left
# parent replaced
if parent.left is node:
parent.left = son
if parent.right is node:
parent.right = son
son.parent = parent
def __deleteNR(self,node):
parent = node.parent
son = node.right
# replace parent
if parent.left is node:
parent.left = son
if parent.right is node:
parent.right = son
son.parent = parent
def __deleteLR(self, node):
minimal = self.getMinimal(node.right)
if minimal.parent.left is minimal:
minimal.parent.left = None
if minimal.parent.right is minimal:
minimal.parent.right = None
# parent of minimal done..
if node.parent.left is node:
node.parent.left = minimal
if node.parent.right is node:
node.parent.right = minimal
minimal.right = node.right
minimal.left = node.left
def getMinimal(self, node):
k = node
while k.left is not None:
k = k.left
return k
def getMax(self):
current = self.root
while current.right:
current = current.right
return current
def __trav(self, node):
if not node:
return
print(node.val)
self.__trav(node.left)
self.__trav(node.right)
def printTrav(self):
self.__trav(self.root)
def __deleteRoot(self):
if self.root.left is None and self.root.right is None:
self.root = None
return
if self.root.left is None and self.root.right is not None:
# left empty,right full
self.root.right.parent = None
self.root = self.root.right
return
if self.root.left is not None and self.root.right is None:
# right empty, left full
self.root.left.parent = None
self.root = self.root.left
return
# node has both children
if self.root.left is not None and self.root.right is not None:
temp = self.getMinimal(self.root.right) # minimal from right subtree
# sometimes it could be like this..
# r
# \
# x
if temp.parent.left is temp:
temp.parent.left = None
else:
temp.parent.right = None
self.root.left.parent = temp
self.root.right.parent = temp
temp.right = self.root.right
temp.left = self.root.left
self.root = temp
self.root.parent = None
return
def search(self, val):
node = self.root
if node.val == val:
return node
if val > node.val and node.right is not None:
node = node.right
if val < node.val and node.left is not None:
node = node.left
else:
print("There's no such value!")
return
def printMax(self):
print(self.getMax().val)
def printMin(self):
print(self.getMinimal(self.root).val)
arr=[None]*2500
for x in range(2500):
arr[x]=Node(random.randint(-10000,10000))
myTree = MyTree(arr[0])
for x in range(1,2500):
myTree.insert(arr[x])
for x in range(2500):
myTree.delete(arr[x])
It is suspicious that you define search twice.
Still that said, here is how I would debug this. I would modify your program to read from a file, try to run, and then detect an endless loop and bail out. Now write random files until you have one that causes you to crash.
Once you have a random file that shows the bug, the next step is to make it minimal. Here is a harness that can let you do that.
import itertools
flatten = itertools.chain.from_iterable
# bug_found should be a function that takes a list of elements and runs your test.
# example should be an array that demonstrates the bug.
def find_minimal (bug_found, example):
parts = [example]
while 1 < max(len(part) for part in parts):
i = 0
while i < len(parts):
if 1 == len(parts[i]):
i = i + 1
else:
part = parts.pop(i)
# Divide in 2.
mid = len(part)/2
part1 = part[0:mid]
part2 = part[mid:len(part)]
# Do we need part1?
parts.insert(i, part1)
if bug_found(flatten(parts)):
i = i + 1
parts.insert(i, part2)
else:
parts[i] = part2
# Do we need part2?
if bug_found_func(flatten(parts)):
i = i + 1
else:
parts.pop(i)
return list(flatten(parts))
Just let it run, and after some time it is likely to find a small example. Which will greatly aid in debugging.
So - I found 2 serious bugs in code. Both in LR ("standard" node and root). As I suspected - bugs were in pointers. Now tree is working (tested few times for 20k,30k and 100k nodes). Solved.
I am finished with the case when the node that i want to remove is the root node or the leaf node, but i need to be able to remove also when it has siblings or children, which i am finding very hard.
class Node:
def __init__(self, key=None, data=None):
self.key = key
self.data = data
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
self.size = 0
def remove(self, key):
self.root = self._remove(key, self.root)
def insert(self, key, data):
self.root = self._insert(self.root, key, data)
def _insert(self, root, key, data):
if root == None:
self.size += 1
return Node(key, data)
if root.key == key:
return root
elif root.key > key:
root.left = self._insert(root.left, key, data)
else:
root.right = self._insert(root.right, key, data)
return root
def _remove(self, key, node):
if node == None:
return None
if key == node.key:
if node.left != None and node.right == None: # if trying to remove root and right side is empty
return node.left
elif node.left == None and node.right != None: # if trying to remove root and left side is empty
return node.right
elif node.left == None and node.right == None: # if trying to remove leaf
return node
# two more cases to check when it has siblings
# iterates recursively in the bst
elif key <= node.key:
node.left = self._remove(key, node.left)
else:
node.right = self._remove(key, node.right)
return node
I posted the whole code so if anyone wants to test in their machine is welcome to do so, or someone can use it for educational purpose.
In the future try to perform some debugging and provide some sample output and expected output.
Consider below
def _remove(self, key, node):
if node == None:
return None
if key == node.key:
if node.left and not node.right: # only left
return node.left
elif node.right and not node.left: # only right
return node.right
elif not node.right and not node.left: # neither
return None
else : # both
inorder_successor = node.right
while inorder_successor.left:
inorder_successor = inorder_successor.left
# remember to replace inorder_successor with it's right child
...
...
return inorder_successor
# iterates recursively in the bst
elif key <= node.key:
node.left = self._remove(key, node.left)
else:
node.right = self._remove(key, node.right)
return node
A few observations about what changed
You check for None using, is != None which is a very non Pythonic way to it. Just check for is and is not instead
The right way to replace a node in a BST that has both children is with the inorder successor (left most descendant of the right child of the deleted node)
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.