how can i make insert method in binary search tree without recursion - python

i wonder how to make insertion method without recursion.
I made it, but there's something wrong. AttributeError: 'NoneType' object has no attribute 'item' occured.
I know what this error means, but I can't understand why this happend. parent is not None... it moves to curr so it can have item... why this happens?
this is my code.
class BSTNode:
def __init__(self, item, left = None, right = None):
self.item = item
self.left = left
self.right = right
class BSTree():
def __init__(self):
self.root = None
def insert(self, data):
parent = None
curr = self.root
while curr is not None:
if data < curr.item:
parent = curr
curr = curr.left
elif data > curr.item:
parent = curr
curr = curr.right
else:
return False
if curr is None:
if parent is None:
newNode = BSTNode(data)
self.root = newNode
if data < parent.item:
newNode = BSTNode(data)
curr = newNode
return True
elif data > parent.item:
newNode = BSTNode(data)
curr = newNode
return True
def search(self, data):
curr = self.root
while curr is not None:
if data < curr.item:
curr = curr.left
elif data > curr.item:
curr = curr.right
else:
return True
return False
def delete(self, data):
parent = None
curr = self.root
while curr is not None:
if data < curr.item:
parent = curr
curr = curr.left
elif data > curr.item:
parent = curr
curr = curr.right
else:
break
if curr is None:
return False
if curr.left is None:
if parent is None:
self.root = curr.right
else:
if data < parent.item:
parent.left = curr.right
else:
parent.right = curr.right
elif curr.right is None:
if parent is None:
self.root = curr.left
else:
if data < parent.item:
parent.left = curr.left
else:
parent.right = curr.left
else:
parentMaxNode = curr
maxNode = curr.left
while maxNode.right is not None:
parentMaxNode = maxNode
maxNode = maxNode.right
curr.item = maxNode.item
if parentMaxNode.right is maxNode:
parentMaxNode.right = maxNode.left
else:
parentMaxNode.left = maxNode.left
return True

I think you have the problem with insert method and i think my code below is work for you
class BSTNode:
def __init__(self, item, left = None, right = None):
self.item = item
self.left = left
self.right = right
class BSTree():
def __init__(self):
self.root = None
def insert(self, item):
# if the tree is empty, the new item becomes the root node
if self.root is None:
self.root = BSTNode(item)
else:
# find the parent node of the new item
parent = self.root
while True:
if item < parent.item:
# if the item is less than the parent node,
# insert the item to the left of the parent node
if parent.left is None:
parent.left = BSTNode(item)
break
else:
parent = parent.left
else:
# if the item is greater than or equal to the parent node,
# insert the item to the right of the parent node
if parent.right is None:
parent.right = BSTNode(item)
break
else:
parent = parent.right

The problem is with this part at the end of the insert function:
if data < parent.item:
newNode = BSTNode(data)
curr = newNode
return True
elif data > parent.item:
newNode = BSTNode(data)
curr = newNode
return True
The statement curr = newNode just assigns a node reference to a variable. Assigning to a variable can never mutate an object (like a node or a list). If you need to mutate a list (like attaching a left or right child to a node), you really need to assign to an attribute of an existing node (the parent).
So that part of the code must be replaced with this:
if data < parent.item:
newNode = BSTNode(data)
parent.left = newNode
return True
elif data > parent.item:
newNode = BSTNode(data)
parent.right = newNode
return True
...which can be shortened to:
newNode = BSTNode(data)
if data < parent.item:
parent.left = newNode
elif data > parent.item:
parent.right = newNode
return True
(Note that at that point it is already guaranteed that data != parent.item)

Related

Linked List-Singly linked list .......why do we use location == 1?

#While I was studying linked list, i came across a doubt. I could not understand while adding a node or removing a node from the end the location was 1.Can anyone help me out?
#Why location == 1 for adding/ removing from end side?
#Please explain !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
class Node:
def __init__(self, value=None):
self.value = value
self.next = None
class SLinkedList:
def __init__(self):
self.head = None
self.tail = None
def __iter__(self):
node = self.head
while node:
yield node
node = node.next
# insert in Linked List
def insertSLL(self, value, location):
newNode = Node(value)
if self.head is None:
self.head = newNode
self.tail = newNode
else:
if location == 0:
newNode.next = self.head
self.head = newNode
elif location == 1:
newNode.next = None
self.tail.next = newNode
self.tail = newNode
else:
tempNode = self.head
index = 0
while index < location - 1:
tempNode = tempNode.next
index += 1
nextNode = tempNode.next
tempNode.next = newNode
newNode.next = nextNode
def deleteNode(self, location):
if self.head is None:
print("The SLL does not exist")
else:
if location == 0:
if self.head == self.tail:
self.head = None
self.tail = None
else:
self.head = self.head.next
elif location == 1:
if self.head == self.tail:
self.head = None
self.tail = None
else:
node = self.head
while node is not None:
if node.next == self.tail:
break
node = node.next
node.next = None
self.tail = node
else:
tempNode = self.head
index = 0
while index < location - 1:
tempNode = tempNode.next
index += 1
nextNode = tempNode.next
tempNode.next = nextNode.next
Inserting at the beginning and end have to be treated specially.
To insert at the beginning you assign the head attribute of the list rather than the next attribute of the previous node (because there is no previous node).
When you insert at the end, you have to update the tail attribute of the list in addition to linking it to the old tail via its next attribute.

How do I fix the adding value in middle of linked list location?

I'm creating a single linked list insert beginning, end, and middle of the linked list. After running code inserting the middle is not working in the linked list changing location value randomly and after running it's not getting.
Can anyone suggest what is wrong with the code:
class Node:
def __init__(self, value=None):
self.value = value
self.next = None
class SLinkedList:
def __init__(self):
self.head = None
self.tail = None
def __iter__(self):
node = self.head
while node:
yield node
node = node.next
def insertsll(self, value, location):
new_node = Node(value)
if self.head is None:
self.head = new_node
self.tail = new_node
else:
if location == 0:
new_node.next = self.head
self.head = new_node
elif location == 1:
new_node.next = None
self.tail.next = new_node
self.tail = new_node
else:
temp_node = self.head
index = 0
while index < location - 1:
temp_node = temp_node.next
index += 1
next_node = temp_node.next
temp_node.next = new_node
new_node.next = next_node
# if temp_node == self.tail:
# self.tail = new_node
sll = SLinkedList()
sll.insertsll(1, 1)
sll.insertsll(2, 1)
sll.insertsll(3, 1)
sll.insertsll(4, 1)
sll.insertsll(0, 0)
sll.insertsll(60, 3)---> run this random changing location not working in correct location
sll.insertsll(50, 4)---> run this random changing location not working in correct location
print([node.value for node in sll])
Output:
[0, 1, 50, 2, 3, 4]
Process finished with exit code 0
I think the iteration in the while loop was a little bit too complicated. I think with this code it is more understandable what the insert does. Also, I don't know why you had the location === 1 check. It seems to be not necessary.
class Node:
def __init__(self, value=None):
self.value = value
self.next = None
class SLinkedList:
def __init__(self):
self.head = None
self.tail = None
def __iter__(self):
node = self.head
while node:
yield node
node = node.next
def insertsll(self, value, location):
new_node = Node(value)
if self.head is None:
self.head = new_node
self.tail = new_node
else:
if location == 0:
new_node.next = self.head
self.head = new_node
else:
temp_node = self.head
index = 0
# Iterate to insert location
while index < location - 1:
index += 1
temp_node = temp_node.next
# Insert new node
new_node.next = temp_node.next
temp_node.next = new_node
# Check if new node is tail
if new_node.next is None:
self.tail = new_node
sll = SLinkedList()
sll.insertsll(1, 1)
sll.insertsll(2, 1)
sll.insertsll(3, 1)
sll.insertsll(4, 1)
sll.insertsll(0, 0)
sll.insertsll(60, 3)
sll.insertsll(50, 4)
print([node.value for node in sll])
Output:
[0, 1, 4, 60, 50, 3, 2]
P.S.: Such a structure really benefits from a length attribute. This can be used to check if an insert operation is even allowed. Probably, you should consider adding it.

traversing in linked list

I was writing the code for traversing through the linked list but not getting the desired output.
The code is provided let me know the problem
class Node :
def __init__(self,data):
self.data = data
self.next = None
class Linklist :
def __init__(self):
self.head = None
def insert(self,newNode):
if self.head is None:
self.head = newNode
else:
lastNode = self.head
while True:
if lastNode is None:
break
lastNode = lastNode.next
lastNode.next = newNode
def printlinklist(self):
if self.head is None:
print("list is empty")
return
currentNode = self.head
while True:
if currentNode is None:
break
print(currentNode.data)
currentNode = currentNode.next
linklist = Linklist()
firstNode = Node("Aradhya")
linklist.insert(firstNode)
secondNode = Node("Sheila")
linklist.insert(secondNode)
thirdNode = Node("Diya")
linklist.insert(thirdNode)
linklist.printlinklist()
You're almost there!! You need to do this simple modification in the insert() method:
def insert(self,newNode):
if self.head is None:
self.head = newNode
else:
lastNode = self.head
while lastNode.next: #<-- change this instead of `while True`
#if lastNode is None: #<--- there is no point for this line
# break #<--- there is no point for this line#
lastNode = lastNode.next
lastNode.next = newNode

BST with connection to parent - loop

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.

How can I make a tree Node object display its contents when printed?

This error is occurring when we call the lookup method. Can anyone say how can it be rectified? I am unable to debug it using the available documents online. This is an implementation of a binary tree class. I know it is something related to the equivalence problem.
import deque
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.data = data
def insert(self, data):
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
else:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
def lookup(self, data, parent=None):
if self.data == data:
return self, parent
if data < self.data:
if self.left is None:
return None
else:
return self.left.lookup(data, parent=self)
else:
if self.right is None:
return None
else:
return self.right.lookup(data, parent=self)
# aka bfs traversal
def level_traversal(self):
root = self
dq = deque()
dq.append(root)
while dq:
root = dq.popleft()
if root.left:
dq.append(root.left)
if root.right:
dq.append(root.right)
print (root.data)
def delete(self, data):
node, parent = self.lookup(data)
if node.children_count() == 0:
if parent.left == node:
parent.left = None
else:
parent.right = None
del node
elif node.children_count() == 1:
if node.left:
n = node.left
else:
n = node.right
if parent:
if parent.left == node:
parent.left = n
else:
parent.right = n
del node
else:
# find the successor
parent = node
successor = node.right
while successor.left:
parent = successor
successor = successor.left
node.data = successor.data
if parent.left == successor:
parent.left = successor.right
else:
parent.right = successor.right
def inorder(self):
if self.left:
self.left.inorder()
print (self.data)
if self.right:
self.right.inorder()
def preorder(self):
print (self.data)
if self.left:
self.left.preorder()
if self.right:
self.right.preorder()
def postorder(self):
if self.left:
self.left.postorder()
if self.right:
self.right.postorder()
print (self.data)
root = Node(8)
root.insert(3)
root.insert(10)
root.insert(1)
root.insert(6)
root.insert(4)
root.insert(7)
root.insert(14)
root.insert(13)
# look up
print (root.lookup(6))
# level traversal
root.level_traversal()
#mirror image
#root.mirror_image()
#root.delete(3)
#root.level_traversal()
# inorder
#root.inorder()
# pre order
#root.preorder()
# postorder
#root.postorder()
# size
#root.size()
#root.dfs()
#print root.height()
This is not an error at all. This happens because you are returning a tuple of objects from your lookup method, and this is just how objects are represented when you print them out. If you don't like this, you can overwrite the __repr__() method.
Try this in your class definition.
Definition for a binary Tree Node
class TreeNode:
def __init__(self, val = 0, left = None, right = None):
self.val = val
self.left = left
self.right = right
## print TreeNode Object
def __repr__(self) -> str:
return '[%s, %r, %r]' % (self.val, self.left, self.right)

Categories