NPTEL MOOC
PROGRAMMING,
DATA STRUCTURES AND
ALGORITHMS IN PYTHON
Week 7, Lecture 4
Madhavan Mukund, Chennai Mathematical Institute
http://www.cmi.ac.in/~madhavan
Dynamic sorted data
Sorting is useful for efficient searching
What if the data is changing dynamically?
Items are periodically inserted and deleted
Insert/delete in sorted list take time O(n)
Like priority queues, move to a tree structure
Binary search tree
For each node with 5
value v
Values in left
subtree < v 2 8
Values in right
subtree > v
1 4 9
No duplicate values
Binary search tree
For each node with 5
value v
Values in left
subtree < v 2 8
Values in right
subtree > v
1 4 9
No duplicate values
Binary search tree
For each node with 5
value v
Values in left
subtree < v 2 8
Values in right
subtree > v
1 4 9
No duplicate values
Binary search tree
For each node with 5
value v
Values in left
subtree < v 2 8
Values in right
subtree > v
1 4 9
No duplicate values
Binary search tree
For each node with 5
value v
Values in left
subtree < v 2 8
Values in right
subtree > v
1 4 9
No duplicate values
Binary search tree
Each node has a value and points to its children
value left right
5 5
8 None
2 8
9 None None
1 None None 1 4 9
4 None None
A better representation
Add a frontier with empty node: all fields None
Empty tree is a single empty node
Leaf node has value that is not None, left and right
children point to empty nodes
Makes it easier to write recursive functions to
traverse the tree
Binary search tree 5
value left right
2 8
5
1 4 9
2 8 None
1 None None 9 None None
4 None None
Binary search tree 5
value left right
2 8
5
1 4 9
2 8
None
1 None 9
None
None None
None None
4 None None
None None
None None None None
None None
None None
None None
The class Tree
class Tree:
def __init__(self,initval=None):
self.value = initval
if self.value:
self.left = Tree()
self.right = Tree()
else:
self.left = None
self.right = None
return()
def isempty(self):
return(self.value == None)
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
1
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
1 2
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
1 2 4
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
1 2 4 5
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
1 2 4 5 8
Inorder traversal
def inorder(self): 5
if self.isempty():
return([])
else:
return(
2 8
self.left.inorder() +
[self.value] +
self.right.inorder()
)
1 4 9
def __str__(self):
return(str(self.inorder())
Lists values in sorted order 1 2 4 5 8 9
Find a value v
Scan the current node
Go left if v is smaller than this node
Go right if v is larger than this node
Natural generalization of binary search
Find a value v
def find(self,v):
if self.isempty():
return(False)
if self.value == v:
return(True)
if v < self.value:
return(self.left.find(v))
else
return(self.right.find(v))
Minimum
Left most node in the tree
5
def minval(self):
# Assume t is not empty 3 7
if self.left == None:
return(self.value) 1 4 9
else:
return(self.left.minval()) 2 8
Maximum
Right most node in the tree
5
def maxval(self):
# Assume t is not empty 3 7
if self.right == None:
return(self.value) 1 4 9
else:
return(self.right.maxval()) 2 8
Insert v
Insert 21
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 91
28 83
Insert v
Insert 21
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 91
28 83
Insert v
Insert 21
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 91
28 83
21
Insert v
Insert 65
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 91
28 83
21
Insert v
Insert 65
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 91
28 83
21
Insert v
Insert 65
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 65 91
28 83
21
Insert v
Insert 91
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 65 91
28 83
21
Insert v
Insert 91
Try to find v
52
If it is not present, add it
where the search fails 37 74
16 44 65 91
28 83
21
Insert v
def insert(self,v):
if self.isempty(): # Add v as a new leaf
self.value = v
self.left = Tree()
self.right = Tree()
if self.value == v: # Value found, do nothing
return
if v < self.value:
self.left.insert(v)
return
if v > self.value:
self.right.insert(v)
return
Delete v
If v is present, delete it Delete 65
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 74
If deleted node has two children,
fill in the hole with 16 44 65 91
self.left.maxval() (or
self.right.minval()) 28 83
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 65
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 74
If deleted node has two children,
fill in the hole with 16 44 65 91
self.left.maxval() (or
self.right.minval()) 28 83
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 65
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 74
If deleted node has two children,
fill in the hole with 16 44 91
self.left.maxval() (or
self.right.minval()) 28 83
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 74
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 74
If deleted node has two children,
fill in the hole with 16 44 91
self.left.maxval() (or
self.right.minval()) 28 83
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 74
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 74
If deleted node has two children,
fill in the hole with 16 44 91
self.left.maxval() (or
self.right.minval()) 28 83
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 74
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 91
If deleted node has two children, 83
fill in the hole with 16 44
self.left.maxval() (or
self.right.minval()) 28
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 37
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 91
If deleted node has two children, 83
fill in the hole with 16 44
self.left.maxval() (or
self.right.minval()) 28
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 37
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 91
If deleted node has two children, 83
fill in the hole with 16 44
self.left.maxval() (or
self.right.minval()) 28
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 37
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 37 91
If deleted node has two children, 83
fill in the hole with 16 44
self.left.maxval() (or
self.right.minval()) 28
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 37
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 28 91
If deleted node has two children, 83
fill in the hole with 16 44
self.left.maxval() (or
self.right.minval()) 28
Delete self.left.maxval()—
must be leaf or have only one child 21
Delete v
If v is present, delete it Delete 37
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 28 91
If deleted node has two children, 83
fill in the hole with 16 44
self.left.maxval() (or
self.right.minval()) 21
Delete self.left.maxval()—
must be leaf or have only one child
Delete v
If v is present, delete it
If deleted node is a leaf, done
52
If deleted node has only one child,
“promote” that child 28 91
If deleted node has two children, 83
fill in the hole with 16 44
self.left.maxval() (or
self.right.minval()) 21
Delete self.left.maxval()—
must be leaf or have only one child
Delete v
def delete(self,v):
if self.isempty():
return
if v < self.value:
self.left.delete(v)
return
if v > self.value:
self.right.delete(v)
return
if v == self.value:
if self.isleaf():
self.makeempty()
elif self.left.isempty():
self.copyright()
else:
self.value = self.left.maxval()
self.left.delete(self.left.maxval())
return
Delete v # Convert leaf to
# empty node
def delete(self,v): def makeempty(self):
if self.isempty(): self.value = None
return self.left = None
self.right = None
if v < self.value:
return
self.left.delete(v)
return
if v > self.value:
self.right.delete(v)
return
if v == self.value:
if self.isleaf():
self.makeempty()
elif self.left.isempty():
self.copyright()
else:
self.value = self.left.maxval()
self.left.delete(self.left.maxval())
return
Delete v # Convert leaf to
# empty node
def delete(self,v): def makeempty(self):
if self.isempty(): self.value = None
return self.left = None
self.right = None
if v < self.value:
return
self.left.delete(v)
return
if v > self.value: # Copy right child values
self.right.delete(v) # to current node
return def copyright(self):
self.value = self.right.value
if v == self.value:
self.left = self.right.left
if self.isleaf():
self.right = self.right.right
self.makeempty()
elif self.left.isempty():
return
self.copyright()
else:
self.value = self.left.maxval()
self.left.delete(self.left.maxval())
return
Complexity
All operations on search trees walk down a single
path
Worst-case: height of the tree
Balanced trees: height is O(log n) for n nodes
Tree can be balanced using rotations — look up
AVL trees