Tree Algorithm Slides
Tree Algorithm Slides
Video Series
Storage and
representation
of trees
Definitions and storage representation
William Fiset
Trees!
What is a tree?
Trees!
What is a tree?
directory
directory directory
directory directory
Trees out in the wild
Social hierarchies
Trees out in the wild
Abstract syntax trees to decompose source
code and mathematical expressions for easy
evaluation.
((x + 6) * -3) > (2 - y)
>
* -
+ -3 2 y
x 6
Trees out in the wild
Every webpage is a tree as an HTML
DOM structure
<html>
<head> <body>
item 1 item 2
Trees out in the wild
The decision outcomes in game theory are often
modeled as trees for ease of representation.
P1
De
es
fe
nf
ct
Co
P2 P2
s
s
De
De
es
es
fe
fe
nf
nf
ct
ct
Co
Co
• Family trees
• File parsing/HTML/JSON/Syntax trees
• Many data structures use/are trees:
• AVL trees, B-tree, red-black trees,
segment trees, fenwick trees, treaps,
suffix trees, tree maps/sets, etc…
• Game theory decision trees
• Organizational structures
• Probabilty trees
• Taxonomies
• etc…
Storing undirected trees
Storing undirected trees
Start by labelling the tree
nodes from [0, n)
2 3 4 5
6 7 8
9
Storing undirected trees
edge list storage
representation:
0 [(0, 1),
(1, 4),
1 (4, 5),
(4, 8),
2 3 4 5 (1, 3),
(3, 7),
6 7 8 (3, 6),
(2, 3),
9 (6, 9)]
Not a
Binary tree Binary tree binary tree
Binary Search Trees (BST)
Related to binary trees are binary search trees
which are trees which satisfy the BST invariant
which states that for every node x:
[Link] ≤ [Link] ≤ [Link]
6
6
3
2 7
3 8
2
1 4 9
1 1 7 9
3 5 8
Not a BST
BST BST
Binary Search Trees (BST)
It’s often useful to require uniqueness on the
node values in your tree. Change the invariant
to be strictly < rather than ≤:
[Link] < [Link] < [Link]
6
6
3
2 7
3 8
2
1 4 9
1 1 7 9
3 5 8
Not a BST
BST BST
Storing rooted trees
Rooted trees are most naturally defined
recursively in a top-down manner.
Storing rooted trees
In practice, you always maintain a pointer
reference to the root node so that you can
access the tree and its contents.
root node
Storing rooted trees
Each node also has access to a list
of all its children.
child nodes
Storing rooted trees
Sometimes it’s also useful to maintain a
pointer to a node’s parent node effectively
making edges bidirectional.
parent
current
node
Storing rooted trees
However, this isn’t usually necessary
because you can access a node’s parent on
a recursive function's callback.
Storing rooted trees
If your tree is a binary tree, you can store
it in a flattened array.
i
8 7 1 2
6 5 1 2 3 4 5 6
2 3 4 2 1 7 8 9 10 11 12 13 14
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
9 8 7 6 5 1 2 ∅ 2 3 4 ∅ ∅ 2 1
6 5 1 2 3 4 5 6
2 3 4 2 1 7 8 9 10 11 12 13 14
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
9 8 7 6 5 1 2 ∅ 2 3 4 ∅ ∅ 2 1
8 7 1 2
6 5 1 2 3 4 5 i 6
2 3 4 2 1 7 8 9 10 11 12 13 14
index tree
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
9 8 7 6 5 1 2 ∅ 2 3 4 ∅ ∅ 2 1
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
9 8 7 6 5 1 2 ∅ 2 3 4 ∅ ∅ 2 1
i
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
9 8 7 6 5 1 2 ∅ 2 3 4 ∅ ∅ 2 1
4 3
1 -6 0 7 -4
2 9 8
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8 - 4 = 9
5
4 3
1 -6 0 7 -4
2 9 8
5
4 3
1 -6 0 7 -4
2 9 8
4 3
1 -6 0 7 -4
2 9 8
5
4 3
1 -6 0 7 -4
2 9 8
5
4 3
1 -6 0 7 -4
2 9 8
5
4 3
1 -6 0 7 -4
2 9 8
2
5
4 3
1 -6 0 7 -4
2 9 8
2
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8 - 4
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8 - 4
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8 - 4
5
4 3
1 -6 0 7 -4
2 9 8
2 + 9 - 6 + 0 + 8 - 4 = 9
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
# Sums up leaf node values in a tree.
# Call function like: leafSum(root)
function leafSum(node):
# Handle empty tree case
if node == null:
return 0
if isLeaf(node):
return [Link]()
total = 0
for child in [Link]():
total += leafSum(child)
return total
function isLeaf(node):
return [Link]().size() == 0
Problem 2: Tree Height
Find the height of a binary tree. The height
of a tree is the number of edges from the root
to the lowest leaf.
height 0
height 1
height 3
Let h(x) be the height of the subtree
rooted at node x.
b c
e
Let h(x) be the height of the subtree
rooted at node x.
b c
b c
b c
e
?
? ?
? ? ? ?
? ?
?
? ?
? ? ? ?
? ?
?
? ?
? ? ? ?
? ?
?
? ?
? ? 0 ?
? ?
? ?
? ? 0 ?
? ?
?
? ?
? ? 0 0
? ?
? 1
? ? 0 0
? ?
height = max(0, 0) + 1 = 1
?
? 1
? ? 0 0
? ?
?
? 1
? ? 0 0
? ?
?
? 1
? 0 0 0
? ?
? 1
? 0 0 0
? ?
?
? 1
? 0 0 0
? ?
?
? 1
? 0 0 0
0 ?
? 1
? 0 0 0
0 ?
?
? 1
? 0 0 0
0 0
? 1
1 0 0 0
0 0
height = max(0, 0) + 1 = 1
?
2 1
1 0 0 0
0 0
height = max(1, 0) + 1 = 2
3
2 1
1 0 0 0
0 0
height = max(2, 1) + 1 = 3
3
2 1
1 0 0 0
0 0
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Handle empty tree case
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Handle empty tree case
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Handle empty tree case
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Handle empty tree case
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Handle empty tree case
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Handle empty tree case
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Return -1 when we hit a null node
# to correct for the right height.
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
Notice that if we visit the null nodes
our tree is one unit taller.
When we go down the tree we need to correct
for the height added by the null nodes.
-1
+1
-1
+1
+1
-1
+1
+1
+1
-1
+1
+1
+1
+1
-1
+1
+1
+1
+1
-1
1 + 1 + 1 + 1 - 1 = 3
# The height of a tree is the number of
# edges from the root to the lowest leaf.
function treeHeight(node):
# Return -1 when we hit a null node
# to correct for the right height.
if node == null:
return -1
return max(treeHeight([Link]),
treeHeight([Link])) + 1
Next Video: rooting a tree
Graph Theory
Video Series
Rooting a
tree
William Fiset
Rooting a tree
Sometimes it’s useful to root an undirected
tree to add structure to the problem you’re
trying to solve.
2 Undirected graph
adjacency list:
3
0 1 0 -> [2, 1, 5]
1 -> [0]
2 -> [3, 0]
4 5 3 -> [2]
4 -> [5]
5 -> [4, 6, 0]
6
6 -> [5]
Rooting a tree
Sometimes it’s useful to root an undirected
tree to add structure to the problem you’re
trying to solve.
3 0
0 1
2 1 5
4 5
3 4 6
6
Rooting a tree
Conceptually this is like "picking up" the
tree by a specific node and having all the
edges point downwards.
3 0
0 1
2 1 5
4 5
3 4 6
6
Rooting a tree
3 5
0 1
0 4 6
4 5
1 2
6
3
In some situations it’s also useful to
keep have a reference to the parent
node in order to walk up the tree.
3 5
0 1
0 4 6
4 5
1 2
6
3
Rooting a tree is easily done depth first.
3
0 1
4 5
6
Rooting a tree is easily done depth first.
3 0
0 1
4 5
6
Rooting a tree is easily done depth first.
3 0
0 1
4 5
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1 2
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1 2
4 5
6 4
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1 2
4 5
6 4 3
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1 2
4 5
6 4 3
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1 2
4 5
6 4 3
6
Rooting a tree is easily done depth first.
3 0
0 1
5 1 2
4 5
6 4 3
6
Rooting tree pseudocode
William Fiset
Center(s) of undirected tree
An interesting problem when you have an
undirected tree is finding the tree’s center
node(s). This could come in handy if we wanted
to select a good node to root our tree 😉
Center(s) of undirected tree
An interesting problem when you have an
undirected tree is finding the tree’s center
node(s). This could come in handy if we wanted
to select a good node to root our tree 😉
center(s)
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Notice that the center is always the middle
vertex or middle two vertices in every
longest path along the tree.
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Notice that the center is always the middle
vertex or middle two vertices in every
longest path along the tree.
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Notice that the center is always the middle
vertex or middle two vertices in every
longest path along the tree.
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Notice that the center is always the middle
vertex or middle two vertices in every
longest path along the tree.
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Notice that the center is always the middle
vertex or middle two vertices in every
longest path along the tree.
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Notice that the center is always the middle
vertex or middle two vertices in every
longest path along the tree.
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Another approach to find the center is to
iteratively pick off each leaf node layer
like we were peeling an onion.
Center(s) of undirected tree
1
1 0 9 1
4
3
4
2 1 2 3
3 6 5
1
7 8 1
1
The orange circles represent the degree of
each node. Observe that each leaf node will
have a degree of 1.
Center(s) of undirected tree
1
1 0 9 1
4
3
4
2 1 2 3
3 6 5
1
7 8 1
1
Center(s) of undirected tree
0
0 0 9 0
4
1
3
1 1 2 3
1 6 5
0
7 8 0
0
1 6 5
0
7 8 0
0
Center(s) of undirected tree
0
0 0 9 0
4
0
0
0 1 2 3
0 6 5
0
7 8 0
0
Center(s) of undirected tree
0 9
4
1 2 3
6 5
7 8
Center(s) of undirected tree
0
2 3 4 5
6 7 8
9
Center(s) of undirected tree
0 1
3
1
4 3
1 2 3 4 5 1
6 7 8
2
1 1
9
1
Center(s) of undirected tree
0 1
3
1
4 3
1 2 3 4 5 1
6 7 8
2
1 1
9
1
Center(s) of undirected tree
0 0
2
1
2 1
0 2 3 4 5 0
6 7 8
1
0 0
9
0
Center(s) of undirected tree
0 0
2
1
2 1
0 2 3 4 5 0
6 7 8
1
0 0
9
0
Center(s) of undirected tree
0 0
1
1
1 0
0 2 3 4 5 0
6 7 8
0
0 0
9
0
Center(s) of undirected tree
0
2 3 4 5
6 7 8
9
Some trees have two centers
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
# g = tree represented as an undirected graph
function treeCenters(g):
n = [Link]()
degree = [0, 0, …, 0] # size n
leaves = []
for (i = 0; i < n; i++):
degree[i] = g[i].size()
if degree[i] == 0 or degree[i] == 1:
[Link](i)
degree[i] = 0
count = [Link]()
while count < n:
new_leaves = []
for (node : leaves):
for (neighbor : g[node]):
degree[neighbor] = degree[neighbor] - 1
if degree[neighbor] == 1:
new_leaves.add(neighbor)
degree[node] = 0
count += new_leaves.size()
leaves = new_leaves
return leaves # center(s)
Graph Theory
Video Series
Isomorphisms
in trees
A question of equality
William Fiset
Graph Isomorphism
The question of asking whether two graphs
G1 and G2 are isomorphic is asking whether
they are structurally the same.
0 a
e b
4 1
3 2 d c
G1 G2
0
1 4
1
2 3
0 2 5 7
4 5
6 7
3 6
0
1 4
2 5
3 4 5
0
1 4
2 5
3 4 5
2 0 2
3 5 3 4
4 6 5
2 0 2
3 5 3 4
4 6 5
2 0 1 2
Identifying Isomorphic Trees
To select a common node between both trees
we can use what we learned from finding the
center(s) of a tree to help ourselves.
1 4 3 4
2
0 1 2
5
0
T1 3 5
T2
1 4 3 4
2
0 1 2
1 4 3 4
2
0 1 2
4 2
1 3 1 4
0 2 5 0 3 5
1 4 3 4
2
0 1 2
4 2
1 3 1 4
0 2 5 0 3 5
((()())(())) ((()())(()))
1 4 3 4
2
0 1 2
4 2
1 3 1 4
0 2 5 0 3 5
000101100111 000101100111
The tree encoding is simply a sequence of left '(' and
right ')' brackets. However, you can also think of them
as 1’s and 0’s (i.e a large number) if you prefer.
5
0
T1 3 5
T2
1 4 3 4
2
0 1 2
4 2
1 3 1 4
0 2 5 0 3 5
((()())(())) ((()())(()))
It should also be possible to reconstruct
the tree solely from the encoding. This is
left as an exercise to the reader… 😛
Generating the tree encoding
2 1 3
6 7 4 5 8
9
Tree Encoding
Start by assigning all leaf nodes
Knuth tuples: '()'
0
2 1 3
6 7 4 5 8
() () () ()
9
()
Tree Encoding
Start by assigning all leaf nodes
Knuth tuples: '()'
0
2 1 3
6 7 4 5 8
() () () ()
9
()
Tree Encoding
Process all nodes with grayed out children and
combine the labels of their child nodes and
wrap them in brackets.
0
(()()) 2 1 (()) 3
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
Process all nodes with grayed out children and
combine the labels of their child nodes and
wrap them in brackets.
0
(()()) 2 1 (()) 3
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
(()()) 2 1 (()) 3
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
Notice that the labels get sorted when
combined, this is important.
0
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
Notice that the labels get sorted when
combined, this is important.
0
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
(((())())(()())(()))
0
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
(((())())(()())(()))
0
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding
(((())())(()())(()))
0
6 7 4 (()) 5 8
() () () ()
9
()
Tree Encoding Summary
labels = []
for child in [Link]():
[Link](encode(child))
result = ""
for label in labels:
result += label
labels = []
for child in [Link]():
[Link](encode(child))
result = ""
for label in labels:
result += label
labels = []
for child in [Link]():
[Link](encode(child))
result = ""
for label in labels:
result += label
labels = []
for child in [Link]():
[Link](encode(child))
result = ""
for label in labels:
result += label
labels = []
for child in [Link]():
[Link](encode(child))
result = ""
for label in labels:
result += label
William Fiset
Previous video explaining
identifying isomorphic trees:
Source Code Link
Implementation source code can
be found at the following link:
[Link]/williamfiset/algorithms
What is a LCA?
Where is it used/found?
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(13, 14) = 2
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(13, 14) = 2
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(10, 16) = 5
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(10, 16) = 5
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(9, 11) = 0
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(9, 11) = 0
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
You can also find the LCA of more than 2 nodes
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(10, LCA(12, 16)) = 2
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Understanding LCA
LCA(10, LCA(12, 16)) = 2
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
Situation matters
There are lots of considerations to take
into account when solving the LCA problem
which should change how you choose to
tackle the problem.
Situation matters
Question 3:
Is the tree static or dynamic? I.e, can
nodes be added or removed?
Question 4:
Do you need an online or offline algorithm?
1 2
3 4 5 6 7
8 9 10 11 12 13
14 15 16
0
0
1 2 1
3 2 3
2*(n-1) + 1 = 2n - 2 + 1 = 2n - 1 nodes
0
1 2
3 4 5
0 1 2 3 4 5 6 7 8 9 10 11 12
depth
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes
0 Depth 0
1 2 Depth 1
3 4 5 Depth 2
6 Depth 3
0 1 2 3 4 5 6 7 8 9 10 11 12
depth
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes
0
1 2
3 4 5
0 1 2 3 4 5 6 7 8 9 10 11 12
depth
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes
0
0
1 2
3 4 5
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0
0
0
1 1 2
3 4 5
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1
0
0
1 1 2
3 4 5
2
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3
0
0
1 1 2
3
3 4 5
2
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1
0
0
4
1 1 2
3
3 4 5
2
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0
0
0
4
1 1 2
3 5
3 4 5
2
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2
0
0
4
1 1 2
3 5
6
3 4 5
2
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4
0
0
4
1 1 2
3 5
6
3 4 5
2
6
7
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6
0
0
4
1 1 2
3 5
6
3 4 8 5
2
6
7
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6 4
0
0
4
1 1 2
3 5
9
6
3 4 8 5
2
6
7
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6 4 2
0
0
4
1 1 2
3 5
9
6
3 4 8 5
2 10
6
7
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1 2
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6 4 2 5
0
0
4 11
1 1 2
3 5
9
6
3 4 8 5
2 10
6
7
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1 2 1
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6 4 2 5 2
0 12
0
4 11
1 1 2
3 5
9
6
3 4 8 5
2 10
6
7
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1 2 1 0
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6 4 2 5 2 0
0 12
0
4 11
1 1 2
3 5
9
6
3 4 8 5
2 10
6
7
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1 2 1 0
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6 4 2 5 2 0
0 12
0 Q: What is LCA(6, 5)?
4 11
1 1 2 1. Find the index position value for the
3 5
nodes `a` and `b` (5 and 6 respectably)
9
6
3 4 8 5
2. Using the depth array, find the
2 10 index of the minimum value in the range
6 of the indices obtained in step 1
7
3. Using the index obtained in step 2,
index into the `nodes` array containing
the tree node data.
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1 2 1 0
0 1 2 3 4 5 6 7 8 9 10 11 12
nodes 0 1 3 1 0 2 4 6 4 2 5 2 0
0 12
0
4 11
1 1 2
3 5
9
6
3 4 8 5
2 10
6
7
If you recall, step 1 was to find the index position
value for the nodes the two nodes with ids `a` and `b`.
6
7
6
7
6
7
6
7
6
7
The reason the selection of the inverse index mapping
doesn’t matter is that it will not affect the value
obtained from the Range Minimum Query (RMQ) in step 2
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1 2 1 0
0 12
0
4 11
1 1 2
3 5
9
6
3 4 8 5
2 10
6
7
Suppose that for the LCA(1, 2) we had selected index
1 for node 1 and index 9 for node 2, meaning the
range [1, 9] in the depth array for the RMQ.
0 1 2 3 4 5 6 7 8 9 10 11 12
depth 0 1 2 1 0 1 2 3 2 1 2 1 0
0 12
0
4 11
1 1 2
3 5
9
6
3 4 8 5
2 10
6
7
William Fiset