I am trying to write a BFS algorithm for a Binary Search Tree using Python3.
I first initialised the class, defined an insert method, and inserted some random numbers:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def insert(self, data):
if data <= self.data:
if self.left == None:
self.left = Node(data)
else:
self.left.insert(data)
else:
if self.right == None:
self.right = Node(data)
else:
self.right.insert(data)
root = Node(1)
root.insert(0)
root.insert(2)
root.insert(1.5)
root.insert(2.4)
root.insert(1.6)
root.insert(2.3)
Then, I tried to write a recursive BFS method:
def inLineTraversal(self, queue=[]):
if queue == []: # Base condition
return
temp = queue
for node in temp:
print(node.data)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
queue.pop(0)
self.inLineTraversal(queue)
However, this produced 1,2,2.4,1.5,2.3,2.3,1.6 as opposed to the correct result of 1,0,2,1.5,2.4,1.6,2.3.
I later used a while loop to perform the BFS, which correctly produced 1,0,2,1.5,2.4,1.6,2.3:
def inLineTraversal(self):
queue = [self]
while queue != []:
s = queue.pop(0)
print(s.data)
if s.left != None:
queue.append(s.left)
if s.right != None:
queue.append(s.right)
What was wrong with the recursive solution?
The problem is in this line:
temp = queue
This doesn't create a new copy of queue, as you probably expected, it just gives it an additional name temp - but both names refer to the same list.
So, further on, you iterate on this list while modifying it, which almost always leads to problems.
Just create a copy of your list, like :
temp = queue[:]
And everything works fine
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def insert(self, data):
if data <= self.data:
if self.left == None:
self.left = Node(data)
else:
self.left.insert(data)
else:
if self.right == None:
self.right = Node(data)
else:
self.right.insert(data)
def inLineTraversalRecursive(self, queue=[]):
if queue == []: # Base condition
return
temp = queue[:]
for node in temp:
print(node.data)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
queue.pop(0)
self.inLineTraversal(queue)
def inLineTraversalLoop(self):
queue = [self]
while queue != []:
s = queue.pop(0)
print(s.data)
if s.left != None:
queue.append(s.left)
if s.right != None:
queue.append(s.right)
root = Node(1)
root.insert(0)
root.insert(2)
root.insert(1.5)
root.insert(2.4)
root.insert(1.6)
root.insert(2.3)
root.inLineTraversalLoop()
print()
root.inLineTraversalRecursive([root])
Output:
1
0
2
1.5
2.4
1.6
2.3
1
0
2
1.5
2.4
1.6
2.3
This is because you put self.inLineTraversal(queue) outside the for loop. Also this line temp = queue is redundant.
So the correct code for your recursive inLineTraversal would be:
def inLineTraversal(self, queue=[]):
if queue == []: # Base condition
return
for node in queue:
print(node.data)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
queue.pop(0)
self.inLineTraversal(queue) # inside of the for loop
I tested and ran this modified method, I got the result you expected: 1,0,2,1.5,2.4,1.6,2.3.
I can see 2 problems in your code
1.) In the InlineTraversal method, You are copying queue to temp array which is not the correct way for copying elements. You are doing shallow copy here. That means both the lists(Queue and Temp) will point to element in same memory location.
2.) You are removing the elements from list inside for loop. you are iterating over Temp list and removing element from queue list which are actually same lists. So, basically you are removing element from same list on which you are iterating which is not a good practice.
To Fix this, perform deep copy on queue list by using
temp = queue[:]
Related
I'm new to python. I have the following list of books stored on the linked list and I want to sort them by using quicksort but unfortunately, I'm stuck on a problem.
class Node:
def __init__(self, data=None):
self.data = data
self.prev = None
self.next = None
class DoublyLinkedList:
def __init__(self):
self.head = None
self.tail = None
def append_value(self, x):
if not isinstance(x, Node):
x = Node(x)
if self.is_empty():
self.head = x
else:
current = self.head
while current.next:
current = current.next
current.next = x
x.prev = current
self.tail = x
def length(self):
count = 0
current = self.head
while current:
count += 1
current = current.next
return count
def is_empty(self):
return self.head is None
def __str__(self):
to_print = ''
current = self.head
while current:
to_print += f'{current.data} <-> '
current = current.next
if to_print:
return f'[{to_print[:-5]}]'
return '[]'
def quick_sort(self, arr):
if self.length() < 2:
return self
else:
pivot = self.tail.data
smaller, equal, larger = [], [], []
current = self.head
while current:
if current.data < pivot:
smaller.append(current.data)
elif current.data == pivot:
equal.append(current.data)
else:
larger.append(current.data)
current = current.next
return self.quick_sort(smaller) + equal + self.quick_sort(larger)
This is the quicksort method but it's giving me RecursionError on 'return self.quick_sort(smaller) + equal + self.quick_sort(larger)'. How do I sort the linked list by using quicksort?
my_list = DoublyLinkedList()
my_list.append_value('In Search of Lost Time')
my_list.append_value('Ulysses by James Joyce')
my_list.append_value('Within a Budding Grove')
my_list.append_value('The Guermantes Way')
my_list.append_value('In Search of Lost Time')
my_list.append_value('Sodom & Gomorrah')
my_list.append_value('One Hundred Years of Solitude')
my_list.append_value('War and Peace')
print(f'List of Books: {my_list}')
print(f'Quick Sort: {my_list.quick_sort(my_list)}')
Indeed, the error occurs because you pass a standard list to quick_sort, while it expects the argument to be a doubly linked list instance.
Moreover, it would be strange if the use of lists were allowed in this algorithm, as then one may wonder why there is a doubly linked list in the first place. If you're going to use lists, then you might as well store the data in a list and sort the list. This cannot be the purpose.
Some other comments about the code:
quick_sort is a method of the class, so it should not be necessary to pass the linked list also as argument. Instead, pass two arguments that identify the start and end node of a subsection in that linked list.
The final return statement is problematic. If indeed quick_sort is going to return a linked list, then + is not an operator you can use on those. Moreover, Quick Sort is supposed to be an in-place sorting algorithm, while + suggests a new list being created. As it is in-place, you should be able to just return self.
So, ... you really need to solve this by walking through the linked list and manipulate it.
Here is one way to do it. First define two methods that allow inserting and deleting a node anywhere in the list:
def remove_node(self, node):
if node.prev:
node.prev.next = node.next
else:
self.head = node.next
if node.next:
node.next.prev = node.prev
else:
self.tail = node.prev
node.next = node.prev = None
return node
def insert_after(self, prev, node):
if not isinstance(node, Node):
node = Node(node)
node.prev = prev
if prev:
node.next = prev.next
prev.next = node
else:
node.next = self.head
self.head = node
if node.next:
node.next.prev = node
else:
self.tail = node
return self
The latter is in fact a more flexible method than the append_value you already had. You could actually make use of this new one to define yours:
def append_value(self, x):
return self.insert_after(self.tail, x)
Then perform the quick sort as follows:
Take a start and end node as arguments. Extract (remove) the end node from the list and consider it the pivot. Let two references walk along this sublist towards each other, starting at the ends. When their values are at the good side of the pivot value, let that reference move closer. Once you have two references where the value is opposite from the pivot, swap the data that is in these two nodes. Once these two references cross each other you have identified the two partitions. Inject the pivot node back into the list, right between those two partitions. Finally, use recursion to do the same with the two partitions:
def quick_sort(self, start=None, end=None):
if not start and not end: # set default arguments:
start = self.head
end = self.tail
if not start or not end or start == end or end.next == start:
# The sublist is empty or has just one value
return self
# Extract the pivot node from the list
end, pivot = end.prev, self.remove_node(end)
pivotdata = pivot.data
# Define two references that will walk towards eachother:
right = end
left = start
while left and left.prev != right: # While they didn't cross over
if left.data <= pivotdata:
left = left.next
elif right.data >= pivotdata:
right = right.prev
else: # Swap values, not the nodes:
left.data, right.data = right.data, left.data
left = left.next
right = right.prev
# Insert the pivot node between the two partitions
self.insert_after(right, pivot)
# Sort the non-empty partitions recursively
if start.prev != pivot:
self.quick_sort(start, right)
if end.next != pivot:
self.quick_sort(left, end)
return self
I am trying this coding problem on leetcode and not able to debug the error!!
It's been 3 hours and I tried re-writing the logic again but I am still missing something. What else can I add to make it work against the test case:
>>>input - [1,2,3,4,null,null,5]
>>>expected output - [[4,5],[2,3],[1]]
Although this code is working for the given test case:
>>> input - [3,9,20,null,null,15,7]
>>> output - [[15,7],[9,20],[3]]
This is my effort:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
queue = []
level = []
result = []
if root is None:
return None
result.append([root.val])
queue.append(root)
while len(queue) > 0:
a = queue[0]
ans = self.traverse_value(a.left, a.right, queue)
if ans is not None:
result.append(ans)
queue.pop(0)
return reversed(result)
def traverse_value(self, r_left, r_right, queue):
if r_left is None and r_right is None: # both l and r are none
return
elif r_left is None and r_right is not None: # right is not none
queue.append(r_right)
return [r_right.val]
elif r_left is not None and r_right is None: # left is not none
queue.append(r_left)
return [r_left.val]
elif r_left is not None and r_right is not None: # both are not none
queue.append(r_left)
queue.append(r_right)
return [r_left.val, r_right.val]
Your code can only create sublists of up to two elements, via the function traverse_value. This cannot be right, since obviously more wide trees will have more elements on the same level. Your algorithm has no provision to put "cousins" in the same list, only direct siblings.
Your BFS approach is certainly a good idea, but make sure to distinguish correctly between layers, so you know when to put values in the same list or in a new one:
result = []
if root is None:
return None
queue = [root]
while len(queue):
# get the values out of the current queue
result.append([a.val for a in queue])
# perform a separate loop for the this layer only
nextlayer = []
for a in queue:
# just extend this new list for the whole layer
if a.left:
nextlayer.append(a.left)
if a.right:
nextlayer.append(a.right)
# finally put that whole layer in the queue
queue = nextlayer
return reversed(result)
For your info, it can also be done with a DFS solution where you just keep track of the depth you are at, and use that as index in the solution list:
level = []
def recur(node, depth):
if not node:
return
if depth >= len(level):
level.append([])
level[depth].append(node.val)
recur(node.left, depth+1)
recur(node.right, depth+1)
recur(root, 0)
level.reverse()
return level
My code is looking like this:
class Treenode:
def __init__(self,data,left=None,right=None):
self.data=data
self.left=left
self.right=right
def __str__(self):
return str(self.data)
def delete(self):
child=self.left
grandchild=child.right
print(grandchild)
if self.left == self.right==None:
return None
if self.left==None:
return self.right
if self.right==None:
return self.left
if grandchild:
while grandchild.right:
child = grandchild
grandchild = child.right
self.data = grandchild.data
child.right = grandchild.left
else:
self.left = child.left
self.data = child.data
return self
class Bintree:
def __init__(self):
self.root = None
def put(self,data):
if self.root == None:
self.root = Treenode(data)
return
p = self.root
while True:
if data < p.data:
if p.left == None:
p.left = Treenode(data)
return
else:
p = p.left
elif data > p.data:
if p.right == None:
p.right = Treenode(data)
return
else:
p = p.right
elif data == p.data:
return False
else:
return
def exists(self, data):
return finns(self.root, data)
def isempty(self):
return self.root == None
def height(self):
def hp(tree):
if tree is None:
return 0
else:
return 1 + max(hp(tree.left), hp(tree.right))
return hp(self.root)
def printTree(self):
skriv(self.root)
def remove(self, data):
if self.root and self.root.data == data: #self.root kanske inte behövs, undersök
self.root = self.root.delete()
return
else:
parent = self.root
while parent:
if data < parent.data:
child = parent.left
if child and child.data== data:
parent.left = child.delete()
return
parent = child
else:
child = parent.right
if child and child.data == data:
parent.right = child.delete()
return
parent = child
def skriv(tree):
if tree == None:
return
skriv(tree.left)
print(tree.data)
skriv(tree.right)
def finns(roten, key):
if roten == None:
return False
if key == roten.data:
return True
elif key < roten.data:
return finns(roten.left, key)
else:
return finns(roten.right, key)
Everything about my code is working, and I've simply added (see copied) the delete method and the remove method. Im desperately trying to understand the delete-method but I cannot understand it. I use this code to run the thing and see how the tree is implemented:
from labb8test import Bintree
from labb8test import Treenode
tree = Bintree()
tree.put(8)
tree.put(3)
tree.put(1)
tree.put(6)
tree.put(4)
tree.put(7)
tree.put(10)
tree.put(14)
tree.put(13)
tree.remove(6)
tree.printTree()
I'm trying to draw it on a paper and see, especially how the while-loop is working. According to my above code, I would think it is like this:
child = self.left (child=3) grandchild= child.right=self,left.right=6. If grandchild (yes, 6) while grandchild.right (yes, 7) child = grandchild, 3-->6 grandchild = child.right (is this even needed, 6--->6?) self.data=grandchild.data (8--->6) child.right = grandchild.left (6---->4) ??
But it cannot be like this, because then the while-loop would never end. Is there anyone who can help me understanding where I lose myself?
I recommend you this material from algorithm Princeton:
http://algs4.cs.princeton.edu/32bst/
The delete method is using this approach to delete a node from a bst.
Delete. We can proceed in a similar manner to delete any node that has
one child (or no children), but what can we do to delete a node that
has two children? We are left with two links, but have a place in the
parent node for only one of them. An answer to this dilemma, first
proposed by T. Hibbard in 1962, is to delete a node x by replacing it
with its successor. Because x has a right child, its successor is the
node with the smallest key in its right subtree. The replacement
preserves order in the tree because there are no keys between x.key
and the successor's key. We accomplish the task of replacing x by its
successor
in four (!) easy steps:
Save a link to the node to be deleted in t
Set x to point to its successor min(t.right)
Set the right link of x (which is supposed to point to the BST containing all the keys larger than x.key) to deleteMin(t.right), the
link to the BST containing all the keys that are larger than x.key
after the deletion.
Set the left link of x (which was null) to t.left (all the keys that are less than both the deleted key and its successor).
I am doing a Python program that implements linked list to support a few functions, one of the functions I need to do is to reverse a stack. I have made a Node, LinkedList and Stack classes, here is my Code so far:
class ListNode:
def __init__(self, Object):
self.Object = Object
self.next = None
class LinkedList:
def __init__(self):
self.head = None # first node
self.tail = None # last node
def addLast(self, Object):
newNode = ListNode(Object)
if self.head == None:
self.head = newNode
self.tail = newNode
else:
self.tail.next = newNode
self.tail = newNode
def removeFirst(self):
if self.head == None:
return
self.head = self.head.next
if self.head == None:
self.tail = None
def removeLast(self, Object):
if self.head == None:
return
current = self.head
prev = None
while current.next != None:
prev = current
current = current.next
if prev == None:
self.head = None
self.tail = None
else:
prev.next = None
self.tail = prev
def get(self, index):
current = self.head
i = 0
while i < index and current != None:
current = current.next
i = i + 1
if current != None and index >= 0:
return current.Object
else:
return None
def size(self):
current = self.head
count = 0
while current != None:
count = count + 1
current = current.next
return count
def isEmpty(self):
return self.head == None
def printList(self):
if self.head != None:
current = self.head
while current != None:
print(current.Object, end = ' ')
current = current.next
print()
# -------------------- STACK ---------------------------------------------------
class Stack:
# constructor implementation
def __init__(self):
self.llist = LinkedList()
def front(self):
return self.llist.get(0)
def dequeue(self):
self.llist.removeFirst()
def queue(self, Object):
self.llist(Object)
def push(self, Object):
self.llist.addLast(Object)
def pop(self, Object):
self.llist.removeLast(Object)
def printStack(self):
self.llist.printList()
def size(self):
return self.llist.size()
def isEmpty(self):
return self.llist.isEmpty()
# ----------------------- Reverse LIST ------------------------------
def Reverse(S):
# S is a Stack Object
Here is my attempt at the problem:
def rRecursive( self ) :
self._rRecursive( self.head )
def _reverseRecursive( self, n ) :
if None != n:
right = n.next
if self.head != n:
n.next = self.head
self.head = n
else:
n.next = None
self._rRecursive( right )
def Reverse(S):
s.rRecursive()
If this was an ordinary list I could easily reverse it using [::-1] but I cannot since the linkedlist is an object. I was thinking maybe I could use temporary values so I can someone append the beginning of the stack to a list and then somehow convert it back into a stack object.
Edit for duplication: The goal of my program is use an existing Stack and reverse it. The linked post deals with a linked list that was already in a list format.
def get(self, index):
current = self.head
i = 0
while i < index and current != None:
current = current.next
i = i + 1
if current != None and index >= 0:
return current.Object
else:
return None
Edit 2: added my get function.
class Stack:
# constructor implementation
def __init__(self):
self.llist = LinkedList()
def front(self):
return self.llist.get(0)
# push method implementation
def push(self, Object):
self.llist.addLast(Object)
def pop1(self):
self.llist.removeLast1()
def Reverse(S):
new_stack = Stack()
while not S.isEmpty():
new_stack.push(S.front())
S.pop1()
return new_stack
# Current Stack after Push: 12 14 40 13
# Stack after Pop: 12 14 40
# Stack after Reversal: 12 12 12
Edit 3: Added a rework of my code, it returns back a wrong reversal with the first element over and over again.
It's quite easy to reverse a stack using basic stack operations. Pop each item off the old stack and push it onto a new one.
def reverse(stack):
new_stack = Stack()
while not stack.isEmpty():
new_stack.push(stack.front())
stack.pop()
return new_stack
You could do something similar to destructively reverse the LinkedList within the Stack while reusing the stack object itself, you'd just need to use the list operations rather than the stack operations that are aliased to them.
Speaking of stack operations, you'll probably find that your stack performs better if you push and pop from the front, rather than the back. Removing an item from the end of the linked list requires iterating over the whole list (to find the next-to-last node). In contrast, both adding and removing from the front are fast.
You shouldn't have to fiddle with link pointers in your reversal function. I assume that you have a pop() method and other basics with your stack; if not, then clone your removeFirst function to return the removed node.
Now, the recursive function is simple: pop the head of the list, reverse the remaining stack (if any), and add the popped node to the end. Does this handle your problem?
def reverseStack(self):
move_me = self.pop()
if not self.isEmpty():
return (self.reverseStack()).addLast(move_me)
else:
new_stack = Stack()
return new_stack.addLast(move_me)
Other approaches to solving this problem: Cheat.
Define a __iter__ method (which makes sense in any event; iteration is a core Python behavior) to make your type iterable. Simple example:
def __iter__(self):
cur = self.head
while cur is not None:
yield cur.Object
cur = cur.next
Then just do:
values = list(self) # Creates a list containing the current set of values
self.head = self.tail = None # Clear existing linked list
# Add back all the values in reverse order
for value in reversed(values):
self.addLast(value)
Sure, it's likely less efficient in memory allocation/deallocation overhead than doing it properly. But the effect is likely marginal, and it simplifies the implementation code dramatically.
Of course, doing it properly isn't that hard, it's just slightly more confusing (totally untested, but it should be close to right):
def reverse(self):
# Start at beginning, which will be new end
cur, last = self.head, None
# Reverse head and tail pointers in advance
self.head, self.tail = self.tail, self.head
# Traverse while reversing direction of each node pointer
while cur is not None:
# Tuple pack and unpack allows one-line variable swap
cur.next, cur, last = last, cur.next, cur
The usual algorithm for reversing things using basic data structures is to use the fact that a stack is a first in last out data structure. That means if you pop until the stack is empty, you will get the items in the opposite order you pushed them on.
But it looks like you want to do this via recursive functions rather than an explicit stack - in this case, your function would normally fetch the front element,then recurse on the rest of the data structure, then handle that first element. This gets all the elements off in order, but handles them in the opposite order as each recursive call finishes and you work your way back up the call stack. If you neex to add the elements to a reversed data structure (and not just print them), you can build it up via return values by noticing that once you have the reverse if everything after the current element, you just need to attach that element to the front and you have the reverse of everything you were handed.
class Node(object):
def __init__(self, data=None, next_node=None):
self.data = data
self.next = next_node
def Reverse(head):
if head == None :
return None
elif head.next == None :
return head # returns last element from stack
else :
temp = head.next # stores next element while moving forward
head.next = None # removes the forward link
li = Reverse(temp) # returns the last element
temp.next = head # now do the reverse link here
return li
def main() :
temp4 = Node(4,None)
temp3 = Node(3,temp4)
temp2 = Node(2,temp3)
head = Node(1,temp2)
res = Reverse(head)
while res != None :
print(res.data)
res = res.next
main()
The end goal is to copy a node from one tree to another tree. I want to visit each node in a binary tree and return a node instance after a number of traverses. I cannot seem to figure out how to return a specific node. Every time the node returned matches the id of the root node since I pass the root node to the function.
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target):
global count
if target == count:
# print 'At target', '.'*25, target
return self
else:
if self.left is not None:
count += 1
self.left.randnode(target)
if self.right is not None:
count += 1
self.right.randnode(target)
If you're doing a DFS and counting iterations, you don't even need recursion, just a stack of places to try, and popping/pushing data.
def dfs(self,target):
count = 0
stack = [start]
while stack:
node = stack.pop()
if count == target:
return node
if node is None: # since we push left/right even if None
continue # stop pushing after we hit None node
stack.extend([self.left,self.right])
return -1 # ran out of nodes before count
Bonus points : swapping stack to a queue for BFS
Apart from that, you might want to pass the count as a parameter, like all self-respecting recursive calls, you can make this stateless ;-)
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target,count=0):
if target == count:
# print 'At target', '.'*25, target
return self
if self.left is not None:
return self.left.randnode(target,count + 1)
if self.right is not None:
return self.right.randnode(target,count + 1)