Lecture 2 (Practive Problems BST)¶
Helpers¶
from collections import deque
class Node:
def __init__(self,val=0,left=None,right=None):
self.val = val
self.left = left
self.right = right
def to_tree(nums):
n = len(nums)
if n ==0:
return None
root = Node(nums[0])
queue = deque([root])
idx =1
while queue:
node = queue.popleft()
# left child
if idx <n:
val = nums[idx]
if val !=-1:
node.left = Node(val)
queue.append(node.left)
idx +=1
# right child
if idx <n:
val = nums[idx]
if val != -1:
node.right = Node(val)
queue.append(node.right)
idx +=1
return root
root = to_tree([1,2,3,-1,5])
print(root)
<__main__.Node object at 0x1044a8b50>
Ceil in a Binary Search Tree¶
Given a Binary Search Tree and a key, return the ceiling of the given key in the Binary Search Tree. Ceiling of a value refers to the value of the smallest node in the Binary Search Tree that is greater than or equal to the given key. If the ceiling node does not exist, return nullptr.
root : [10,5,15,2,6] , key=7
ouput : 10
reason : smallest value >= 7 is 10
root:[8,5,12,4,7,10,14,-1,-1,6,-1,-1,-1,13] key = 9
output : 10
Approach 1 (Use BST property)¶
def find_ceil(root,target):
def search(node):
if node is None:
return None
if node.val == target:
return target
if node.val < target:
return search(node.right)
better = search(node.left)
return better if better is not None else node.val
return search(root)
print(find_ceil(to_tree([10,5,15,2,6]),7))
print(find_ceil(to_tree([8,5,12,4,7,10,14,-1,-1,6,-1,-1,-1,13]),9))
10 10
Complexity¶
- N : no of nodes in BST
- O(log N)
- O (log N)
Floor in a Binary Search Tree¶
Given a Binary Search Tree and a key, return the floor of the given key in the Binary Search Tree. Floor of a value refers to the value of the largest node in the Binary Search Tree that is smaller than or equal to the given key. If the floor node does not exist, return nullptr.
root : [10,5,15,2,6] , target: 7
output : 6
root: [ 8, 5, 12, 4, 7, 10, 14, -1, -1, 6, -1, -1, -1, 13] , target:9
output:8
Approach 1(Use BST Property)¶
def find_floor(root,target):
def search(node):
if node is None:
return None
if node.val == target:
return target
if node.val > target:
return search(node.left)
floor_right = search(node.right)
return floor_right if floor_right is not None else node.val
return search(root)
print(find_floor(to_tree([10,5,15,2,6]),7))
print(find_floor(to_tree([8, 5, 12, 4, 7, 10, 14, -1, -1, 6, -1, -1, -1, 13]),9))
6 8
Complexity¶
- N : no of nodes in BST
- O(log N)
- O(log N)
Insert into a Binary Search Tree¶
You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST.
Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them.
root: [4,2,7,1,3], target : 5
output: [4,2,7,1,3,5]
root: [40,20,60,10,30,50,70] target: 25
output: [40,20,60,10,30,50,70,-1,-1,25]
Approach 1 (create insert method)¶
- keep searching the leaf node which satisfied the condition
- Rebuilds the path on return.
def insert_bst(root,target):
def insert(node):
if node is None:
return Node(target)
if target < node.val:
node.left = insert(node.left)
else:
node.right = insert(node.right)
return node
return insert(root)
print(insert_bst(to_tree([4,2,7,1,3]),5))
print(insert_bst(to_tree([40,20,60,10,30,50,70]),25))
<__main__.Node object at 0x1044ab210> <__main__.Node object at 0x104756190>
Complexity¶
- N : no of nodes in BST
- O(log N)
- O(log N)
Approach 2 (Get rid of helper funciton)¶
def insert_bst(root,target):
if root is None:
return Node(target)
if target < root.val:
root.left = insert_bst(root.left,target)
else:
root.right = insert_bst(root.right,target)
return root
print(insert_bst(to_tree([4,2,7,1,3]),5))
print(insert_bst(to_tree([40,20,60,10,30,50,70]),25))
<__main__.Node object at 0x1044a2350> <__main__.Node object at 0x103406a50>