Lists and Arrays just store sequential data
We often have to deal with hierarchical data.
Today, it’s all about
Actually…
In Computer Science, a tree is an abstract data type that represents hierarchical data with a set of connected nodes.
A tree is a connected, acyclic, undirected Graph.
Let \(G = (V,E)\) be a graph with
What makes \(G = (V,E)\) a tree is
Let \(G = (V,E)\) be a tree.
Räcke (2019)
Let \(G = (V,E)\) be a tree with a root \(r \in V\).
Räcke (2019)
Let \(G = (V,E)\) be a tree with a root \(r \in V\).
Räcke (2019)
Important terminology:
Remember that we like to organize and search stuff?
So far, we have used
Arrays:
Lists:
Trees can be used for search:
But, we need to organize them first.
Introducing:
A binary search tree is
It is ordered such that
(We assume that all node-values are different)
Searching in Binary Search Trees is basically binary search.
Disclaimer: The following illustration are made by Harald Räcke (2019, 2021)
Find “17”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “17”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “17”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “17”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “17”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “17”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “17”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “8”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “8”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “8”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “8”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “8”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “8”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
Find “8”
Tree-Search(x, key)
while x ≠ NIL and key ≠ x.key do
if key < x.key then
x := x.left
else
x := x.right
end if
repeat
return x
By the way, the same procedure can be implemented using recursion:
Recursive-Tree-Search(x, key)
if x = NIL or key = x.key then
return x
if key < x.key then
return Recursive-Tree-Search(x.left, key)
else
return Recursive-Tree-Search(x.right, key)
end if
In Binary Search Trees, finding the minimum is simple:
BST-Minimum(x)
while x.left ≠ NIL do
x := x.left
repeat
return x
BST-Minimum(x)
while x.left ≠ NIL do
x := x.left
repeat
return x
BST-Minimum(x)
while x.left ≠ NIL do
x := x.left
repeat
return x
BST-Minimum(x)
while x.left ≠ NIL do
x := x.left
repeat
return x
BST-Minimum(x)
while x.left ≠ NIL do
x := x.left
repeat
return x
New nodes are always inserted as leaf nodes.
Insert “20”
BST-Insert(T, z)
y := NIL
x := T.root
while x ≠ NIL do
y := x
if z.key < x.key then
x := x.left
else
x := x.right
end if
repeat
z.parent := y
if y = NIL then
T.root := z
else if z.key < y.key then
y.left := z
else
y.right := z
end if
Insert “20”
BST-Insert(T, z)
y := NIL
x := T.root
while x ≠ NIL do
y := x
if z.key < x.key then
x := x.left
else
x := x.right
end if
repeat
z.parent := y
if y = NIL then
T.root := z
else if z.key < y.key then
y.left := z
else
y.right := z
end if
Insert “20”
BST-Insert(T, z)
y := NIL
x := T.root
while x ≠ NIL do
y := x
if z.key < x.key then
x := x.left
else
x := x.right
end if
repeat
z.parent := y
if y = NIL then
T.root := z
else if z.key < y.key then
y.left := z
else
y.right := z
end if
Insert “20”
BST-Insert(T, z)
y := NIL
x := T.root
while x ≠ NIL do
y := x
if z.key < x.key then
x := x.left
else
x := x.right
end if
repeat
z.parent := y
if y = NIL then
T.root := z
else if z.key < y.key then
y.left := z
else
y.right := z
end if
Insert “20”
BST-Insert(T, z)
y := NIL
x := T.root
while x ≠ NIL do
y := x
if z.key < x.key then
x := x.left
else
x := x.right
end if
repeat
z.parent := y
if y = NIL then
T.root := z
else if z.key < y.key then
y.left := z
else
y.right := z
end if
Insert “20”
BST-Insert(T, z)
y := NIL
x := T.root
while x ≠ NIL do
y := x
if z.key < x.key then
x := x.left
else
x := x.right
end if
repeat
z.parent := y
if y = NIL then
T.root := z
else if z.key < y.key then
y.left := z
else
y.right := z
end if
Deletions from a binary search tree, case 1: deleting a leaf node
Deletions from a binary search tree, case 1: deleting a leaf node
Case 2: deleting a node with exactly one child
Case 2: deleting a node with exactly one child
Case 2: deleting a node with exactly one child
Case 3: deleting an element with two children.
Case 3 requires to reorganize the tree:
What is the successor and how can we find it?
The successor of a node \(x \in V\): the smallest node greater than \(x\).
BST-Successor(x)
if x.right ≠ NIL then
return BST-Minimum(x.right)
end if
y := x.parent
while y ≠ NIL and x = y.right do
x := y
y := y.parent
repeat
return y
Either obtain the minimum from the subtree on the right.
BST-Successor(x)
if x.right ≠ NIL then
return BST-Minimum(x.right)
end if
y := x.parent
while y ≠ NIL and x = y.right do
x := y
y := y.parent
repeat
return y
In other cases, that is not as trivial:
BST-Successor(x)
if x.right ≠ NIL then
return BST-Minimum(x.right)
end if
y := x.parent
while y ≠ NIL and x = y.right do
x := y
y := y.parent
repeat
return y
BST-Successor(x)
if x.right ≠ NIL then
return BST-Minimum(x.right)
end if
y := x.parent
while y ≠ NIL and x = y.right do
x := y
y := y.parent
repeat
return y
BST-Successor(x)
if x.right ≠ NIL then
return BST-Minimum(x.right)
end if
y := x.parent
while y ≠ NIL and x = y.right do
x := y
y := y.parent
repeat
return y
BST-Successor(x)
if x.right ≠ NIL then
return BST-Minimum(x.right)
end if
y := x.parent
while y ≠ NIL and x = y.right do
x := y
y := y.parent
repeat
return y
Now that we can find the successor reliably,
we can delete an
inner element with two children:
BST-Delete(BST, z)
if z.left = NIL then
Shift-Nodes(BST, z, z.right)
else if z.right = NIL then
Shift-Nodes(BST, z, z.left)
else
y := BST-Successor(z)
if y.parent ≠ z then
Shift-Nodes(BST, y, y.right)
y.right := z.right
y.right.parent := y
end if
Shift-Nodes(BST, z, y)
y.left := z.left
y.left.parent := y
end if
Shift-Nodes(BST, u, v)
if u.parent = NIL then
BST.root := v
else if u = u.parent.left then
u.parent.left := v
else
u.parent.right := v
end if
if v ≠ NIL then
v.parent := u.parent
end if
Assume we have \(n\) elements and a tree with height \(h\)
Implementation | Search | Insert | Delete |
---|---|---|---|
sorted array | \(O( \log n)\) | \(O(n)\) | \(O(n)\) |
unsorted list | \(O(n)\) | \(O(1)\) | \(O(n)\) |
binary search tree | \(O(h)\) | \(O(h)\) | \(O(h)\) |
Some observations:
With all the conceptual advantages of trees,
how are
they actually modelled in programs?
With a lot of pointers…
Acknowledgement:
This script is inspired by Tantau (2016) and Räcke (2019, 2021).