Minimum depth of a binary tree - python

I'm trying to write code to find the minimum depth of a binary tree.
https://leetcode.com/problems/minimum-depth-of-binary-tree/
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def minDepth(self, node):
if node is None:
return 0
else :
# Compute the depth of each subtree
lDepth = self.minDepth(node.left)
rDepth = self.minDepth(node.right)
return min(lDepth, rDepth) + 1
However, this solution does not work on some test cases, such as a highly unbalanced binary tree, which devolves to a linked list (ex [2, None, 3, None, 4, None, 5, None, 6]
The minimum depth is 5 (as None children do not count.) However, my solution returns 1, so it must be treating the left child of 2 as the minimum depth leaf node.
2
/ \
3
/ \
4
/ \
5
/ \
6
I'm at a loss, why does this solution not adequately address this use case?

I think its because of the min function. Starting from 2, your code checks the left which is 0, then checks right which is recursively checked and returns 4. However, you call min(0, 4)+1 which gives you an output of 1.

Your code stops recursion on None which is not a TreeNode, obviously, the term "depth" is undefined for such objects. Try to stop your recursion on so-called "leafs": nodes without children. Check out my solution for this problem:
def is_leaf(node: TreeNode):
return node.left is None and node.right is None
def min_depth(root: TreeNode):
if is_leaf(root):
return 1
not_none_children = (
child if child is not None
for child in (root.left, root.right)]
)
return min(min_depth(child) for child in not_none_children) + 1

#Avinash, thanks for ID'ing the edge case.
And the problem #kellyBundy https://leetcode.com/problems/minimum-depth-of-binary-tree/submissions/
class Solution(object):
def minDepth(self, node):
# no node (dead branch)
if node is None:
return 0
lDepth = self.minDepth(node.left)
rDepth = self.minDepth(node.right)
if node.left and node.right:
return min(lDepth, rDepth) + 1
else: #prevents counting first dead branch as minDepth
return max(lDepth, rDepth) + 1

Related

Preorder Binary Tree traversal Recursive method

I was trying to understand implementation of Binary Tree traversal (PreOrder). The non recursive approach was fine, but I am totally lost while trying to understand the recursive approach.
Code :
def preorder_print(self, start, traversal): """Root->Left->Right"""
if start:
traversal += (str(start.value) + "-")
traversal = self.preorder_print(start.left, traversal)
traversal = self.preorder_print(start.right, traversal)
return traversal
Binary Tree
8
/ \
4 5
/ \ \
2 1 6
My understanding is while reaching Node 2(8-4-2), left of that node 2 is None. So if start: condition would fail.
Below are my questions.
After node2.left is None, how node2.right is traversed? (because if start: condition fails)
After node1, how the logic moves to node5 which rootNode.right?
My understanding on recursion is poor, kindly help!
Watch this, see if this helps:
class Node(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def addleft(self,value):
self.left = Node(value)
def addright(self,value):
self.right = Node(value)
def preorder_print(self, start, traversal='', depth=0):
print( " "*depth, start.value if start else "None" )
if start:
traversal += (str(start.value) + "-")
print(' '*depth, "check left")
traversal = self.preorder_print(start.left, traversal, depth+1)
print(' '*depth, "check right")
traversal = self.preorder_print(start.right, traversal, depth+1)
return traversal
base = Node(8)
base.addleft(4)
base.left.addleft(2)
base.left.addright(1)
base.addright(5)
base.right.addright(6)
print( base.preorder_print( base ) )
Output:
8
check left
4
check left
2
check left
None
check right
None
check right
1
check left
None
check right
None
check right
5
check left
None
check right
6
check left
None
check right
None
8-4-2-1-5-6-

Sum of binary tree leaves' values

I wrote this code and when I use print I see that I get the leaves. However, the final return from the function is None and not the sum of the leaves, which is supposed to be 7 in this example. I'd be happy to know whats wrong here. Thank you !
class Node:
def __init__(self, val=None):
self.left = None
self.right = None
self.val = val
def sum_leafs(tree):
if tree is None:
return 0
if tree.right and tree.left:
sum_leafs(tree.right)
sum_leafs(tree.left)
elif tree.right or tree.left:
if tree.right:
sum_leafs(tree.right)
elif tree.left:
sum_leafs(tree.left)
elif tree.right is None and tree.left is None:
return sum_leafs(tree.left) + 1
node = Node(10)
node.right = Node(2)
node.left = Node(11)
node.left.right = Node(5)
print(sum_leafs(node))
You forgot to add + when you sum the branches (left/right) and also you forgot to access val which is the most crucial thing for the whole thing to work.
Further, the logic can be simplified:
def sum_leafs(tree):
if tree is None:
return 0
if not tree.right and not tree.left:
return tree.val
return sum_leafs(tree.right) + sum_leafs(tree.left)
You are not adding the sums together or returning them. This can also be done with a method in the class:
class Node:
def __init__(self, val=None):
self.left = None
self.right = None
self.val = val
def sum(self):
s = 0
if self.left is not None:
s += self.left.sum()
if self.right is not None:
s += self.right.sum()
return self.val + s
node = Node(10)
node.right = Node(2)
node.left = Node(11)
node.left.right = Node(5)
print(node.sum())
returns:
28
You are not properly returning the calculated leaf sums. Try this:
class Node:
def __init__(self, val=None):
self.left = None
self.right = None
self.val = val
def sum_leafs(tree):
if tree is None:
return 0
elif tree.right and tree.left:
return sum_leafs(tree.right) + sum_leafs(tree.left)
elif tree.right or tree.left:
if tree.right:
return sum_leafs(tree.right)
elif tree.left:
return sum_leafs(tree.left)
elif tree.right is None and tree.left is None:
return tree.val
node = Node(10)
node.right = Node(2)
node.left = Node(11)
node.left.right = Node(5)
print(sum_leafs(node))
7
node
First I'm going to update your Node interface so that it's possible to set left and right branches when creating nodes -
class Node:
def __init__(self, val=None, left=None, right=None):
self.left = left
self.right = right
self.val = val
This allows us to create tress more ergonomically, such as -
t = Node(10, Node(11, None, Node(5)), Node(2))
traverse
Now we write a generic traverse procedure. This allows us to separate 1) the traversal of our tree from 2) the intended operation we want to perform on each tree element -
def traverse(tree):
if tree is None:
return
else:
yield tree.val
yield from traverse(tree.left)
yield from traverse(tree.right)
Now the need for sum_leafs disappears. We have decoupled traversal logic from summing logic. We can calculate the sum of leafs with a simple combination of sum and traverse -
print(sum(traverse(t)))
# 28
don't repeat yourself
Or, instead of summing the values, we could write a search function to find the first value that passes a predicate -
def search(test, tree):
for val in traverse(tree):
if test(val):
return val
print(search(lambda x: x < 10, t))
# 5
print(search(lambda x: x > 99, t))
# None
Or, we could simply collect each value into a list -
print(list(traverse(t)))
# [ 10, 11, 5, 2 ]
As you can see, removing the traversal logic from each function that depends on our tree can be a huge help.
without generators
If you don't like generators, you can write the eager version of traverse which always returns a list. The difference now is there is no way to partially traverse the tree. Note the similarities this program shares with the generator version -
def traverse(t):
if t is None:
return [] # <-- empty
else:
return \
[ t.val
, *traverse(t.left) # <-- yield from
, *traverse(t.right) # <-- yield from
]
print(traverse(t))
# [ 10, 11, 5, 2 ]

Successor and Predecessor - Binary Search Tree (Python)

I am trying out this successor and predecessor on Binary search tree.
Just wondering once I get hold of successor code, can I flip it over and use it for predecessor?
I've coded the successor, and tried to use it for predecessor.
However, my output value doesn't change, even though I tried to place other value..
Below is my code:
Succ
def succ(self, key):
temp = self.root
prev = None
if (temp.right is not None):
temp = temp.right
while (temp.left is not None):
temp = temp.left
prev = temp
elif temp.left is not None:e
temp = temp.left
while (temp.right is not None):
temp = temp.right
prev = temp
else:
return None
return prev.key
Predecessor
def pred(self, key):
temp = self.root
prev = None
#if right is not none, succ lies in the right sub tree
if (temp.right is not None):
temp = temp.right
while (temp.left is not None):
#return the node with the minimum key value
temp = temp.left
prev = temp
#if left not none, succ lies in the right sub tree
elif temp.left is not None:
#go right till .right is None
#return the node with the maximum key value
temp = temp.left
while (temp.right is not None):
temp = temp.right
prev = temp
else:
#no succ
return None
return prev.key
the tree
def createTree(self):
#root
self.put("F",6)
#leftSubTree
self.put("D",4)
#leftLeftSubTree
self.put("C",3)
self.put("B",2)
self.put("A",1)
#LeftRightSubTree
self.put("E",5)
#RightSubTree
self.put("I",9)
#RightLeftSubTree
self.put("G",7)
self.put("H",8)
#RightRightSubTree
self.put("J",10)
To flip the succ function and turn it into pred, you need to change every left to a right, and every right to a left.
def pred(self, key):
temp = self.root
prev = None
if (temp.left is not None):
temp = temp.left
while (temp.right is not None):
temp = temp.right
prev = temp
elif temp.right is not None:
temp = temp.right
while (temp.left is not None):
temp = temp.left
prev = temp
else:
return None
return prev.key
Let us assume you have a BST node class with three pointers/references: left, right, and parent, which correspond to the left child, right child, and parent of a given node. The only node in the tree which has a parent that points to None would be the root node.
Let us also assume that we have the following BST:
15
/ \
9 20
/ \ / \
3 10 17 21
/ \ \
1 5 11
The BST property states that for any given node n, all nodes in n's left sub-tree shall be less than n; and, all nodes in n's right sub-tree shall be greater than n.
To make things easier when implementing the successor and predecessor functions, it is helpful to have auxiliary functions for finding the minimum and maximum node of a given BST or BST sub-tree.
Minimum
def bst_minimum(tree):
minimum = tree
while minimum is not None:
minimum = minimum.left
return minimum
Maximum
def bst_maximum(tree):
maximum = tree
while maximum is not None:
maximum = maximum.right
return maximum
For the tree example above, these functions would return 1 for the minimum and 21 for the maximum.
To find the predecessor of a given node, you have to cover a few cases:
If the given node has a left sub-tree, then take the maximum of that sub-tree.
Otherwise, move up the tree, following parent nodes until either you hit None or you "turn left."
In the second case, if you hit None, that means there is no predecessor. This would be the case for the node with value 1 in the tree above. It would following parent pointers all the way past the root node.
If there is a predecessor, then it will be the first parent node you encounter after making a left turn up the tree. Put another way, it is the parent node whose value is less than the value of the node from which you started. So, node 17 above would return the root node with a value of 15.
Predecessor
def bst_predecessor(tree):
if tree.left is not None:
return bst_maximum(tree.left)
parent = tree.parent
child = tree
while parent is not None and child is parent.left:
child = parent
parent = child.parent
return parent
Since the successor is simply the symmetrical operation to predecessor, you can modify predecessor by flipping the various operations. Namely:
Instead of checking the left sub-tree and finding the maximum, you want to check the right tree and find its minimum.
Otherwise, follow parent nodes until you can't anymore (in which case there is no successor), or you turn right. So, the successor of node 5, would be node 9 in the tree above.
Successor
def bst_successor(tree):
if tree.right is not None:
return bst_minimum(tree.right)
parent = tree.parent
child = tree
while parent is not None and child is parent.right:
child = parent
parent = child.parent
return parent

Implementing Binary Search Tree (Python)

I have the task to perform some basic operations on Binary Search Trees and I'm not sure what is the clever way to do it.
I know that the usual way would be to write a class for the nodes and one for the tree so that I can build up my tree from given values and perform certain tasks on it. The thing is, I'm already getting the tree as a list and since BSTs are not unique, there won't come any good from it if I take each value and build the tree myself.
So... I'm getting a list like this:
11 9 2 13 _, 4 18 2 14 _, 2 10 _ 11 4, 14 16 4 _ _, 13 0 11 _ _ | 10 | 7
which means:
key value parent left right, ... | value1 | value2
So as you see the BST is given explicitly. My tasks are to do a level-print of the tree, return the path from root to value1, do a rotate-right operation on the subtree that has value1, then delete value1 and then insert value2.
What would be an efficient way to tackle this problem?
Here is one possible way of implementing the tree. Hope it helps. Though this contains insertions and popular traversals, not rotations or deletions.
Reference: http://www.thelearningpoint.net/computer-science/learning-python-programming-and-data-structures/learning-python-programming-and-data-structures--tutorial-20--graphs-breadth-and-depth-first-search-bfsdfs-dijkstra-algorithm-topological-search
'''
Binary Search Tree is a binary tree(that is every node has two branches),
in which the values contained in the left subtree is always less than the
root of that subtree, and the values contained in the right subtree is
always greater than the value of the root of the right subtree.
For more information about binary search trees, refer to :
http://en.wikipedia.org/wiki/Binary_search_tree
'''
#Only for use in Python 2.6.0a2 and later
from __future__ import print_function
class Node:
# Constructor to initialize data
# If data is not given by user,its taken as None
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
# __str__ returns string equivalent of Object
def __str__(self):
return "Node[Data = %s]" % (self.data,)
class BinarySearchTree:
def __init__(self):
self.root = None
'''
While inserting values in a binary search tree, we first check
whether the value is greater than, lesser than or equal to the
root of the tree.
We initialize current node as the root.
If the value is greater than the current node value, then we know that
its right location will be in the right subtree. So we make the current
element as the right node.
If the value is lesser than the current node value, then we know that
its right location will be in the left subtree. So we make the current
element as the left node.
If the value is equal to the current node value, then we know that the
value is already contained in the tree and doesn't need to be reinserted.
So we break from the loop.
'''
def insert(self, val):
if (self.root == None):
self.root = Node(val)
else:
current = self.root
while 1:
if (current.data > val):
if (current.left == None):
current.left = Node(val)
break
else:
current = current.left
elif (current.data < val):
if (current.right == None):
current.right = Node(val)
break
else:
current = current.right
else:
break
'''
In preorder traversal, we first print the current element, then
move on to the left subtree and finally to the right subree.
'''
def preorder(self, node):
if (node == None):
return
else:
print(node.data, end=" ")
self.preorder(node.left)
self.preorder(node.right)
'''
In inorder traversal, we first move to the left subtree, then print
the current element and finally move to the right subtree.
'''
#Important : Inorder traversal returns the elements in sorted form.
def inorder(self, node):
if (node == None):
return
else:
self.inorder(node.left)
print(node.data, end=" ")
self.inorder(node.right)
'''
In postorder traversal, we first move to the left subtree, then to the
right subtree and finally print the current element.
'''
def postorder(self, node):
if (node == None):
return
else:
self.postorder(node.left)
self.postorder(node.right)
print(node.data, end=" ")
tree = BinarySearchTree()
tree.insert(1)
tree.insert(9)
tree.insert(4)
tree.insert(3)
tree.insert(5)
tree.insert(7)
tree.insert(10)
tree.insert(0)
print ("Preorder Printing")
tree.preorder(tree.root)
print("\n\nInorder Printing")
tree.inorder(tree.root)
print("\n\nPostOrder Printing")
tree.postorder(tree.root)
Here is the implementation of Binary Search Tree with it's basic operations like insert node, find node
class Node:
def __init__(self,data):
self.left = None
self.right = None
self.data = data
class BST:
def __init__(self):
self.root = None
def set_root(self,data):
self.root = Node(data)
def insert_node(self,data):
if self.root is None:
self.set_root(data)
else:
n = Node(data)
troot = self.root
while troot:
if data < troot.data:
if troot.left:
troot = troot.left
else:
troot.left = n
break
else:
if troot.right:
troot = troot.right
else:
troot.right = n
break
def search_node(self,data):
if self.root is None:
return "Not found"
else:
troot = self.root
while troot:
if data < troot.data:
if troot.left:
troot = troot.left
if troot.data == data:
return "Found"
else:
return "Not found"
elif data > troot.data:
if troot.right:
troot = troot.right
if troot.data == data:
return "Found"
else:
return "Not found"
else:
return "Found"
tree = BST()
tree.insert_node(10)
tree.insert_node(5)
tree.insert_node(20)
tree.insert_node(7)
print(tree.root.data)
print(tree.root.left.data)
print(tree.root.right.data)
print(tree.root.left.right.data)
print(tree.search_node(10))
print(tree.search_node(5))
print(tree.search_node(20))
print(tree.search_node(7))
print(tree.search_node(12))
print(tree.search_node(15))
Output:
10
5
20
7
Found
Found
Found
Found
Not found
Not found
In this specific case I had success using a dictionary as a datatype to store the graph. The key is the node_key and the value is a list with the attributes of the node. In this way it is rather fast to find the needed nodes and all its attributes.
I'm just not sure if there is a way to make it reasonably faster.

Depth of a Binary Search Tree in Python

In my binary search tree I am making a depth function which will tell the user what is the depth of the tree they have insert. This function will be crucial for my unique delete function that deletes a node from the largest depth-sided node. I think I know where the problem is exactly but I'm not sure.
This is my error I keep receiving.
C:\Python33\python.exe "C:/Users/koopt_000/Desktop/College/Sophomore Semester 2/Computer Science 231/Chapter7/Test.py"
Traceback (most recent call last):
PRE-ORDER TRANSVERSE:
File "C:/Users/koopt_000/Desktop/College/Sophomore Semester 2/Computer Science 231/Chapter7/Test.py", line 19, in <module>
4
print("The max depth of the tree is,", a.height(tree),"nodes deep.")
2
File "C:\Users\koopt_000\Desktop\College\Sophomore Semester 2\Computer Science 231\Chapter7\BinarySearchTree.py", line 245, in height
1
3
7
6
5
10
None
IN-ORDER TRANSVERSE:
1
2
3
4
5
6
7
10
None
POST-ORDER TRANSVERSE:
1
return max(BST.height(root.left), BST.height(root.right)) + 1
3
TypeError: height() missing 1 required positional argument: 'root'
2
5
6
10
7
4
None
Process finished with exit code 1
Now I Think my problem arises in this section of code:
return max(BST.height(root.left), BST.height(root.right)) + 1
I believe this statement is what is causing it due to the fact that it is making me call the BST function which the height function is already in for it to 'work'. I simply tried "height.(root.left)", which did not work because it said there is no global variable height. Which isn't the case I don't believe.
Here is my full list of code for my function, starting with my tree node, then my BST file (main), and then my test code.
class TreeNode(object):
def __init__(self, data = None, left=None, right=None):
self.item = data
self.left = left
self.right = right
def __str__(self):
return str(self.item)
from TreeNode import TreeNode
class BST(object):
#------------------------------------------------------------
def __init__(self):
"""create empty binary search tree
post: empty tree created"""
self.root = None
self.size = 0
def delete(self, item):
"""remove item from binary search tree
post: item is removed from the tree"""
self.root = self._subtreeDelete(self.root, item)
#------------------------------------------------------------
def _subtreeDelete(self, root, item):
if root is None: # Empty tree, nothing to do
return None
if item < root.item: # modify left
root.left = self._subtreeDelete(root.left, item)
elif item > root.item: # modify right
root.right = self._subtreeDelete(root.right, item)
else: # delete root
if root.left is None: # promote right subtree
root = root.right
elif root.right is None: # promote left subtree
root = root.left
else:
# root node can't be deleted, overwrite it with max of
# left subtree and delete max node from the subtree
root.item, root.left = self._subtreeDelMax(root.left)
return root
def _subtreeDelMax(self, root):
if root.right is None: # root is the max
return root.item, root.left # return max and promote left subtree
else:
# max is in right subtree, recursively find and delete it
maxVal, root.right = self._subtreeDelMax(root.right)
return maxVal, root
def height(self, root):
if root is None:
return 0
else:
return max(BST.height(root.left), BST.height(root.right)) + 1
from BinarySearchTree import BST
from TreeNode import TreeNode
tree = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode (7, TreeNode(6),TreeNode(9)))
a = BST()
a._subtreeInsert(tree, 10)
a._subtreeInsert(tree, 5)
a._subtreeDelete(tree, 9)
print("PRE-ORDER TRANSVERSE:")
print(a.preOrder(tree))
print("IN-ORDER TRANSVERSE:")
print(a.inOrder(tree))
print("POST-ORDER TRANSVERSE:")
print(a.postOrder(tree))
print("The max depth of the tree is,", a.height(tree),"nodes deep.")
print("There are,", a.treeSize(tree),"nodes in this tree.")
Does anyone see the reason why my height function isn't working properly? Thanks!
Your function height is an instance method of the class BST, you need to call it via self and not with the class BST.
So in your particular case this is:
def height(self, root):
if root is None:
return 0
else:
return max(self.height(root.left), self.height(root.right)) + 1
However, your function height actually does not rely on any data associated directly with the search tree. self is only needed to continue the recursion. Thus, you could also turn it into static method via the staticmethod decorator:
#staticmethod
def height(root):
if root is None:
return 0
else:
return max(BST.height(root.left), BST.height(root.right)) + 1
Alternatively, you could also move the function out of the BST class entirely and get rid off BST.height and just call it via height.
From the code you posted, this holds for basically all functions of the BST class. I don't really see a need for it. You may only use the TreeNode and some top-level functions (without the BST class) in your python module to modify and interact with your tree.

Categories