How to fix yield usage in this BST implementation? - python

I would like to get values from tree as generator to iterate over them. In this case yield just gives me a single value. Function print_tree works as expected - it shows values from smallest to biggest. How to get similar result, but with catching them one by one? This is my BST implementation:
class Node:
def __init__(self, element):
self.left = None
self.right = None
self.data = element
def add_element(self, element):
if self.data > element:
if self.left is None:
self.left = Node(element)
else:
self.left.add_element(element)
else:
if self.right is None:
self.right = Node(element)
else:
self.right.add_element(element)
def print_tree(self):
if self.left: self.left.print_tree()
print(self.data, end=' ')
if self.right: self.right.print_tree()
def get_values(self):
if self.left: self.left.get_values()
yield self.data
if self.right: self.right.get_values()
def get_structure(self):
return (self.left.get_structure() if self.left else None, self.data, self.right.get_structure() if self.right else None)
Code for testing:
from node import *
x = Node(3)
x.add_element(5)
x.add_element(1)
x.add_element(7)
print(x.get_structure()) #result: ((None, 1, None), 3, (None, 5, (None, 7, None)))
x.print_tree() #result: 1 3 5 7
z = x.get_values()
print(list(z)) #result: [3]
Could you please explain me where am I making a mistake and how can I correct it?

First you yield all elements from the left subtree, then the node value and finally all elements from the right subtree
def get_values(self):
if self.left:
yield from self.left.get_values()
yield self.data
if self.right:
yield from self.right.get_values()

Related

Binary search tree - why does it not work without the "return" statement

When I want to search for a name which is in the tree, without the return statement I only get None but why?
See code comment #<----
class Tree:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def child(self, data):
if self.data == data:
return
if self.data < data:
if self.right:
self.right.child(data)
else:
self.right = Tree(data)
elif self.data > data:
if self.left:
self.left.child(data)
else:
self.left = Tree(data)
def search(self, elem):
if self.data == elem:
return True
if self.data < elem:
if self.right:
return self.right.search(elem) #<----
else:
self.right = False
elif self.data > elem:
if self.left:
return self.left.search(elem) #<----
else:
self.left = False
Without return, you ignore the result of the subtree search and fall through to the end of the method, at which point you return None implicitly.
You also need to return False when the appropriate subtree is empty, not set the subtree reference to False.
def search(self, elem):
if self.data == elem:
# Found it!
return True
elif self.data < elem:
# If it exists, it's in the right subtree
return self.right is not None and self.right.search(elem)
else:
# If it exists, it's in the left subtree
return self.left is not None and self.left.search(elem)

Binary Search Tree Delete method Deletes Whole Subtree

I have been learning data structures and have been trying to implement the delete method in my BST class. What I have noticed is that instead of deleting one node it will delete the whole subtree that the node is a part of.
class BST:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def addchild(self, child):
if child == self.data:
return
if child < self.data:
if self.left:
self.left.addchild(child)
else:
self.left = BST(child)
else:
if self.right:
self.right.addchild(child)
else:
self.right = BST(child)
def search(self, data):
if self.data == data:
return True
if data < self.data:
if self.left:
return self.left.search(data)
else:
return False
else:
if self.right:
return self.right.search(data)
else:
return False
def iot(self):
vals = []
if self.left:
vals += self.left.iot()
vals.append(self.data)
if self.right:
vals += self.right.iot()
return vals
def findmax(self):
if self.right:
return self.right.findmax()
else:
return self.data
def findmin(self):
if self.left:
return self.left.findmin()
else:
return self.data
def delete(self, data):
if data < self.data:
if self.left:
self.left = self.left.delete(data)
elif data > self.data:
if self.right:
self.right = self.right.delete(data)
else:
if self.left is None and self.right is None:
return None
elif self.left is None:
return self.right
elif self.right is None:
return self.left
minval = self.right.findmin()
self.data = minval
self.right = self.right.delete(minval)
def buildtree(arr):
r = BST(arr[0])
for i in range(1, len(arr)):
r.addchild(arr[i])
return r
So when input with these values and deleting the number 1 from the tree it will print this
bst = buildtree([34, 7, 8, 1, 3, 4, 5, 10, 65, 98, 100, 203])
print(bst.iot())
bst.delete(1)
print(bst.iot())
1st print statement output: [1, 3, 4, 5, 7, 8, 10, 34, 65, 98, 100, 203]
2nd print statement output: [34, 65, 98, 100, 203]
When I debug in Pycharm, before leaving the delete method and after all steps are executed the tree will show that the left subtree is intact and everything is how it should be. However once I leave the method the left subtree becomes None.
Any help would be appreciated.
Here's a strong hint:
You are missing a return statement on final block of you code:
minval = self.right.findmin()
self.data = minval
self.right = self.right.delete(minval)
A debug answer:
In your example 1 is going to be the leftmost leaf in the tree.
So look what’s happening:
When you start the method delete you say that
if data < self.data:
if self.left:
self.left = self.left.delete(data)
So basically the left son of the root will hold whatever comes at the end of the process. If you will follow the left branch until you get 1, you will see that you get the the case of
else:
if self.left is None and self.right is None:
return None
This means that the root left son is None, and this explains your bug

How can I convert an array of integers into a BST? - in particular how can I initialise the BST, then add nodes to this root?

I am given an array of integers and I would like to convert into a BST;
class BST:
def __init__(self,value):
self.right = None
self.left = None
self.value = value
def insert(self, value):
if value<self.value:
if not self.left:
self.left = BST(value)
else:
self.left.insert(value)
else:
if not self.right:
self.right = BST(value)
else:
self.right.insert(value)
return self
array = [3,10,5,2,7,6,11]
def insertArrayEntryIntoBst(array):
currentNode = BST()
for i in range(len(array)):
currentNode.insert(array[i])
Challenges that I have:
How do I initialise the BST? - in the insert() function do I need to start with a line that reads if not currentNode: BST(array[0])?
After initialising, is my code correct for insertArrayEntryIntoBst()? The idea is simply to loop through the input array and let the insert() function do its magic.
Do I need a value argument in this case? - since the integer value in the array will represent both the node and its value? (which will always be the same thing)
You may construct first node outside loop with the first item of the array.
If you need to access the node. So it can be returned as well.
class BST:
def __init__(self,value):
self.right = None
self.left = None
self.value = value
def insert(self, value):
if value<self.value:
if not self.left:
self.left = BST(value)
else:
self.left.insert(value)
else:
if not self.right:
self.right = BST(value)
else:
self.right.insert(value)
return self
def insertArrayEntryIntoBst(array):
currentNode = BST(array[0])
for i in range(1,len(array)):
currentNode.insert(array[i])
return(currentNode)
array = [3,10,5,2,7,6,11]
myNode=insertArrayEntryIntoBst(array)
print(myNode.value);
print(myNode.left.value);
print(myNode.right.value);

how do I print the value of nodes in a binary tree, that have been added to a queue?

I am writing code to solve the following Leetcode solution:
https://leetcode.com/problems/symmetric-tree/
#THIS FIRST CHUNK OF CODE IS JUST TO BUILD A BINARY TREE!
from collections import deque
class TreeNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def insert(self, val):
# Compare the new value with the parent node
if self.val:
if val <= self.val:
if self.left is None:
self.left = TreeNode(val)
else:
self.left.insert(val)
elif val >= self.val:
if self.right is None:
self.right = TreeNode(val)
else:
self.right.insert(val)
else:
self.val = val
def PrintTree(self):
if self.left:
self.left.PrintTree()
print(self.val),
if self.right:
self.right.PrintTree()
#THIS IS THE CODE TO SOLVE THE LEETCODE PROBLEM
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
queue = deque()
if not root:
return []
if root.left:
queue.append(root.left)
if root.right:
queue.append(root.right)
right_subt = []
left_subt = []
while queue:
level_length = len(queue)
for _ in range(level_length // 2):
node = queue.popleft()
left_subt.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
for _ in range((level_length - (level_length // 2))):
node = queue.popleft()
right_subt.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
print(queue)
if left_subt != right_subt.reverse():
return False
return True
root = TreeNode(1)
root.insert(2)
root.insert(2)
root.insert(3)
root.insert(4)
root.insert(4)
root.insert(3)
root.PrintTree()
x=Solution()
Solution.isSymmetric(x,root)
My code fails the first input: root = [1,2,2,3,4,4,3] ; it should return True but it is retuning False, and I am trying to debug it.
In my code above I build a tree using class TreeNode, and I try to print the queue, however what I get is: deque([<__main__.TreeNode object at 0x7fe9381dc340>, <__main__.TreeNode object at 0x7fe9381dc820>])
Any ideas how I can print the queue to show the node values?
Most elegant would be to define the __repr__ function for the TreeNode class, e.g.
def __repr__(self):
return f"Node {self.val}"
This determines how the TreeNode class is printed. In this case you get
deque([Node 2, Node 3])
Then you can also adapt it according to your needs, e.g. if you want to print left and right in addition to the value.

How to yield all elements on each depth level of BST?

I am trying to create generator which will give me values from next depth level of BST. Let's say that our tree will look like:
I would like to get result for following code like:
for level in tree:
print(level)
# 3
# (1,5)
# (2,4,7)
I was testing different variations of yield usage, but none of them gave me expected result. Here is code which I ended with:
class Node:
def __init__(self, element):
self.left = None
self.right = None
self.data = element
def __iter__(self):
yield self.data
if self.left and self.right:
yield from self.left.__iter__() and self.right.__iter__()
elif self.left:
yield from self.left.__iter__()
elif self.right:
yield from self.right.__iter__()
else:
pass
def add_element(self, element):
if self.data > element:
if self.left is None:
self.left = Node(element)
else:
self.left.add_element(element)
else:
if self.right is None:
self.right = Node(element)
else:
self.right.add_element(element)
def get_structure(self):
return (self.left.get_structure() if self.left else None, self.data, self.right.get_structure() if self.right else None)
This is given example from top:
from node import *
tree = Node(3)
tree.add_element(5)
tree.add_element(1)
tree.add_element(2)
tree.add_element(4)
tree.add_element(7)
print('Structure:',tree.get_structure()) #result: (None, 1, (None, 2, None)), 3, ((None, 4, None), 5, (None, 7, None)))
print('Iteration:', end=" ") #reslut: 3 5 7 / expected result: 3 (1,5) (2,4,7)
for level in tree:
print(level, end=" ")
Explain me please how can I fix my __iter__ method to get values from each depth level of tree?

Categories