how to iterate through a binary search tree in python(no recursion) - python

So far I have
def tree_iterate():
parent, current = None, self.root
lst = []
while current is not None:
if current.left not None:
lst.append(current.item)
parent, current = current, current.left
if current.right not None:
lst.append(current.item)
parent, current = current, current.right
(sorry about spacing I'm quite new at this)
I'm not quite sure how to iterate on both sides of the tree when current has left and right, without using recursion. My main goal is to have a list of all the nodes in this BSTenter code here

To get a list of all nodes in the BST iteratively, use Breadth-First Search (BFS). Note that this won't give you the nodes in sorted order:
queue = [root]
result = []
while queue:
l = queue.pop(0)
result.append(l)
if l.left != None:
queue.append(l.left)
if l.right!= None:
queue.append(l.right)
If you want the nodes in sorted order, you will need to simulate inorder traversal using a stack:
result = []
stack = [root]
while stack:
stack[-1].visited = True
if stack[-1].left != None and not stack[-1].left.visited:
stack.append(stack[-1].left)
else:
node = stack.pop()
result.append(node)
if stack[-1].right != None:
stack.append(stack[-1].right)

Related

Sort entire Linked List into Ascending order based on String Value - Python

I have been trying to implement a Linked List in Python, and have been given this task of sorting it based on of the string values present in the Linked List.
I am trying to use the bubble sort logic and the below function is what I have come up with:
def sort_linked_list(self):
temp = self.head_node
if(temp == None) | (temp.next == None):
return
while(temp is not None):
if(temp.song.song_name > temp.song.song_name):
prev = temp
temp = temp.next
temp.next = prev
temp = temp.next
return
Unfortunately, this iterates over the first two values in my Linked List and is stuck in an infinite loop. I am pretty new to Linked List, hence might be making some fundamental mistake in this loop which I am unable to identify.
Any help is appreciated. Request to please add the explanation to the solution so that I can understand.
The code for the Linked List part:
class Song:
def __init__(self, song_id, song_name, song_length):
self.song_id = song_id
self.song_name = song_name
self.song_length = song_length
def __str__(self):
return str({'song_id':self.song_id,
'song_name':self.song_name,
'song_length':self.song_length})
# Node for each of teh cong object that is going to be created.
# Each of these node will contain the song data nd reference to the next element
class ListNode:
def __init__(self, song:Song):
self.song = song
self.next = None
def __str__(self):
return str(self.song)
# Linked list class that will be used to do the various operations
# Insert, create, delete, traversal of the linked list
# Few other operation such as
# a. deletion of song,
# b. sorting a linked list based on song
# c. randomly picking a song for playing
class LinkedList:
def __init__(self):
self.head_node = None
self.count = 0
# Traversing the linked lists
def traversal(self):
if self.head_node is None:
return
temp_node = self.head_node
while(temp_node != None):
print(temp_node.song)
time.sleep(2)
temp_node = temp_node.next
time.sleep(2)
return
# insertion of the node in the beginning of the linked lists
def insert_at_start(self, node):
if self.head_node is None:
self.head_node = node
self.count = self.count + 1
return True
node.next = self.head_node
self.head_node = node
return True
# insertion of the node after a particular song
def insert_after(self, song_name, node):
temp_node = self.head_node
# Checking till we find the song_name we are looking for
while(temp_node.song.song_name!=song_name):
temp_node = temp_node.next
# if song is not found
if temp_node is None:
return False
# if song is found
else:
# Chckinhg if it is the last node
if temp_node.next == None:
temp_node.next = node
# If it is not the last node
else:
node.next = temp_node.next
temp_node.next = node
return True
# insertion of the node before a particular song in the linked lists
def insert_before(self, song_name, node):
temp_node = self.head_node
prev_node = None
# Checking till we find the song_name we are looking for
while(temp_node.song.song_name!=song_name):
prev_node = temp_node
temp_node = temp_node.next
# if song is not found
if temp_node == None:
return False
# if list has only one song
if prev_node == None:
node.next = self.head_node
self.head_node = node
return True
# updating the linked list and inserting the data
prev_node.next = node
node.next = temp_node
return True
A few issues with your attempt:
The algorithm only makes one visit to every node. It is not possible to sort a list in just one sweep. Bubble sort needs two loops (nested). The outer loop keeps looping for as long as the inner loop had to make swaps. Once the inner loop does not find any pair to swap, the list is sorted, and the outer loop can stop.
A swap of two adjacent nodes in general needs two references to change. The next attribute of the node that sits before that pair needs to be changed as well, and your code is not doing that.
Sorting may mean that that what was originally the head node, no longer sits at the head, and so your code must foresee an assignment to self.head_node... or else the "sorted" version will always start with the same node as before the sort happened.
I would suggest to first create a dummy node that sits before the head node. This helps to simplify the code when the head node needs to be swapped. In that case the dummy's next attribute will change just like any other node's next can change. When all is done, we can then read what is the node that comes after the dummy, and make it the new head node.
def sort_linked_list(self):
sentinel = ListNode(None)
sentinel.next = self.head_node
dirty = True
while dirty: # Repeat until the inner loop finds nothing to swap
dirty = False
node = sentinel
# keep comparing the pair that follows after(!) `node`
while node.next and node.next.next:
first = node.next
second = first.next
if first.song.song_name > second.song.song_name:
# A swap needs to set two(!) next attributes
node.next = second
first.next = second.next
second.next = first
dirty = True # Indicate that the outer loop needs another iteration
node = node.next
# Make sure the head node references the right node after sorting
self.head_node = sentinel.next
Here is a demo which quickly creates a few nodes with your classes and then runs the above method and finally traverses the sorted list:
# Your code without change, except in the `sort_linked_list` method
class Song:
def __init__(self, song_id, song_name, song_length):
self.song_id = song_id
self.song_name = song_name
self.song_length = song_length
def __str__(self):
return str({'song_id':self.song_id,
'song_name':self.song_name,
'song_length':self.song_length})
class ListNode:
def __init__(self, song:Song):
self.song = song
self.next = None
def __str__(self):
return str(self.song)
class LinkedList:
def __init__(self):
self.head_node = None
self.count = 0
def traversal(self):
if self.head_node is None:
return
temp_node = self.head_node
while(temp_node != None):
print(temp_node.song)
temp_node = temp_node.next
return
def insert_at_start(self, node):
if self.head_node is None:
self.head_node = node
self.count = self.count + 1
return True
node.next = self.head_node
self.head_node = node
return True
def sort_linked_list(self):
sentinel = ListNode(None)
sentinel.next = self.head_node
dirty = True
while dirty: # Repeat until the inner loop finds nothing to swap
dirty = False
node = sentinel
# keep comparing the pair that follows after(!) `node`
while node.next and node.next.next:
first = node.next
second = first.next
if first.song.song_name > second.song.song_name:
# A swap needs to set two(!) next attributes
node.next = second
first.next = second.next
second.next = first
dirty = True # Indicate that the outer loop needs another iteration
node = node.next
# Make sure the head node references the right node after sorting
self.head_node = sentinel.next
# Demo
titles = [
"Smells Like Teen Spirit",
"Billie Jean",
"Stayin’ Alive",
"I Will Survive",
"Whole Lotta Love",
"Sweet Child O’Mine",
"Scream and Shout",
"Santeria",
"Alright",
"Thrift Shop"
]
lst = LinkedList()
for i, title in enumerate(titles):
lst.insert_at_start(ListNode(Song(i, title, len(title))))
lst.sort_linked_list()
lst.traversal() # Outputs the 10 songs in sorted order

Removing a node from binary tree

I'm currently working on leetcode problem 366 where we have to find list of lists that contains values of leaves of each generation. I wanted to achieve this by recursion where if a node does not have left or right child, the value is recorded then the node removed by setting it to None. Here is my code:
def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
leaf_list = []
sub_list = []
def traverse(node):
if node == None:
return
if node.left == None and node.right == None:
sub_list.append(node.val)
node = None
return
traverse(node.left)
traverse(node.right)
return root
while True:
if root == None:
break
sub_list = []
traverse(root)
leaf_list.append(sub_list)
print(leaf_list)
return leaf_list
The problem seems to be that when a certain node is set to None, that change isn't retained. Why is it that I can't set a node to None to remove it?
Thanks
The tree can only be mutated when you assign to one if its node's attributes. An assignment to a variable, only changes what the variable represents. Such assignment never impacts whatever previous value that variable had. Assigning to a variable is like switching from one value to another without affecting any data structure. So you need to adapt your code such that the assignment of None is done to a left or right attribute.
The exception is for the root node itself. When the root is a leaf, then there is no parent to mutate. You will then just discard the tree and switch to an empty one (None).
One way to achieve this, is to use the return value of traverse to update the child-reference (left or right) that the caller of traverse needs to update.
Here is your code with those adaptations:
def findLeaves(root):
sub_list = []
def traverse(node):
if not node:
return
if not node.left and not node.right:
sub_list.append(node.val)
return # By returning None, the parent can remove it
node.left = traverse(node.left) # Assign the returned node reference
node.right = traverse(node.right)
return node # Return the node (parent does not need to remove it)
leaf_list = []
while root:
sub_list = []
root = traverse(root)
leaf_list.append(sub_list)
return leaf_list

Implementing DFS and BFS for binary tree

I'm trying to traverse a binary tree using depth first traversal and breadth first traversal, but I'm running into trouble. My node and tree implementation seems to be fine, I'm just not sure how to properly traverse the tree depth-wise and breadth-wise.
class Node:
def __init__(self, val):
self.l = None
self.r = None
self.v = val
class Tree:
def __init__(self):
self.root = None
def getRoot(self):
return self.root
def add(self, val):
if(self.root == None):
self.root = Node(val)
else:
self._add(val, self.root)
def _add(self, val, node):
if(val < node.v):
if(node.l != None):
self._add(val, node.l)
else:
node.l = Node(val)
else:
if(node.r != None):
self._add(val, node.r)
else:
node.r = Node(val)
def find(self, val):
if(self.root != None):
return self._find(val, self.root)
else:
return None
def _find(self, val, node):
if(val == node.v):
return node
elif(val < node.v and node.l != None):
self._find(val, node.l)
elif(val > node.v and node.r != None):
self._find(val, node.r)
def printTree(self):
if(self.root != None):
self._printTree(self.root)
def _printTree(self, node):
if(node != None):
self._printTree(node.l)
print(str(node.v) + ' ')
self._printTree(node.r)
# This doesn't work - graph is not subscriptable
def dfs(self, graph, start):
visited, stack = set(), [start]
while stack:
vertex = stack.pop()
if vertex not in visited:
visited.add(vertex)
stack.extend(graph[vertex] - visited)
return visited
# Haven't tried BFS. Would use a queue, but unsure of the details.
If it is a tree, visited can be a list since trees are non-circular, so there no need to check whether you have visited a node before and, more importantly, you want to maintain the order of your traversal.
def dfs(self, tree):
if tree.root is None:
return []
visited, stack = [], [tree.root]
while stack:
node = stack.pop()
visited.append(node)
stack.extend(filter(None, [node.r, node.l]))
# append right first, so left will be popped first
return visited
Your DFS implementation is slightly incorrect. As written, you've actually mimicked a queue, not a stack.
Your current code actually works fairly well for breadth-first search. It forces the siblings of a node to be evaluated before its children:
def bfs(self, graph, start):
visited, queue = set(), [start]
while queue:
vertex = queue.pop()
if vertex not in visited:
visited.add(vertex)
# new nodes are added to end of queue
queue.extend(graph[vertex] - visited)
return visited
The logic for DFS requires a stack to behave like this: when a new node comes, you need to add it to the left of the list, rather than the right. This way, you force the traversal of a node's descendants before the node's siblings.
def dfs(self, graph, start):
visited, stack = set(), [start]
while stack:
vertex = stack.pop()
if vertex not in visited:
visited.add(vertex)
# new nodes are added to the start of stack
stack = graph[vertex] - visited + stack
return visited
Other Issues
The specific issue you are facing beyond this is that you haven't specified what graph is.
If graph is an object that doesn't support lookup, then you could implement that using a __getitem__() method in the class definition.
Typically, people are content to use a dictionary to implement this. Something like {Node: [<list of node's children], ... } should more than suffice.

BST problems in python

class BST:
"""A Binary Search Tree."""
def __init__(self: 'BST', container: list =None) -> None:
"""
Initialize this BST by inserting the items from container (default [])
one by one, in the order given.
"""
# Initialize empty tree.
self.root = None
# Insert every item from container.
if container:
for item in container:
self.insert(item)
def insert(self: 'BST', item: object) -> None:
"""
Insert item into this BST.
"""
# Find the point of insertion.
parent, current = None, self.root
while current:
if item < current.item:
parent, current = current, current.left
else: # item > current.item
parent, current = current, current.right
# Create a new node and link it in appropriately.
new_node = _BSTNode(item)
if parent:
if item < parent.item:
parent.left = new_node
else: # item > parent.item
parent.right = new_node
else:
self.root = new_node
this is the code that I built for the BST class, I would like implement a max_node function that finds the maximum node without using recursion , how am I suppose to do that?
In a BST, the maximum node is simply the right-most node, so start at the head and just keep taking right children until you hit a node with no right child. This can easily be done iteratively (and in fact, that's probably how I would do it anyway).
In pseudocode:
max_node = head
while hasRightChild(max_node)
max_node = max_node.right_child;

Problem building a complete binary tree of height 'h' in Python

Here is my code. The complete binary tree has 2^k nodes at depth k.
class Node:
def __init__(self, data):
# initializes the data members
self.left = None
self.right = None
self.data = data
root = Node(data_root)
def create_complete_tree():
row = [root]
for i in range(h):
newrow = []
for node in row:
left = Node(data1)
right = Node(data2)
node.left = left
node.right = right
newrow.append(left)
newrow.append(right)
row = copy.deepcopy(newrow)
def traverse_tree(node):
if node == None:
return
else:
traverse_tree(node.left)
print node.data
traverse_tree(node.right)
create_complete_tree()
print 'Node traversal'
traverse_tree(root)
The tree traversal only gives the data of root and its children. What am I doing wrong?
The main problem here is that you are using deepcopy on the temporary list. Consider what happens each iteration:
Your initial root gets inspected, and child nodes get created
These child nodes are placed in newrow
Copies of these child nodes are copied into row for the next iteration.
This means the subsequent iteration will not be mutating the nodes you created (and which root.left and root.right points to), but copies of them, leaving the originals in their current state (with None for .left and .right)

Categories

Resources