You are on page 1of 75

Trees, Part 1

CSCI 2720
University of Georgia
Spring 2007
Tree Properties and Definitions
Composed of nodes and edges
Node
Any distinguishable object
Edge
An ordered pair <u,v> of nodes
Illustrated by an arrow with tail at u and head at v
u = tail of <u,v>
v = head of <u,v>
Root
A distinguished node, depicted at the top of the tree
Recursive Definition of a Tree
1. A single node, with no edges, is a tree. The
root of the tree is its unique node.
2. Let T
1
, , T
k
(k >=1) be trees with no nodes
in common, and let r
1
, ,r
k
be the roots of
those trees, respectively. Let r be a new
node. Then there is a tree T consisting of the
nodes and edges of T
1
, , T
k
, the new node
r and the edges <r,r
1
>,,<r,r
k
>. The root of
T is r and T
1
, ,T
k
are called the subtrees of
T.
Tree definitions and terminology
r is called the parent of r
1
, , r
k
r
1
, , r
k
are the children of r
r
1
, , r
k
are the siblings of one another
Node v is a descendant of u if
u = v or
v is a descendant of a child of u
A path exists from u to v.
Recursive definition
Before:
The trees T
1
, , T
k
,
with roots r
1
, , r
k
After:
new tree T rooted at r
with subtreesT1, , Tk,
What do we mean by a path from u to v?
You can get from u to v by following a
sequences of edges down the tree, starting
with an edge whose tail is u, ending with an
edge whose head is v, and with the head of
each edge but the last being the tail of the
next.

Tree terminology
Every node is a descendant of itself.
If v is a descendant of u, then u is an
ancestor of v.
Proper descendants:
The descendants of a node, other than the
node itself.
Proper ancestors
The ancestors of a node, other than the
node itself.
Tree terminology & properties
Leaf
a node with no children
Num_nodes = Num_edges + 1
Every tree has exactly one more node than
edge
Every edge, except the root, is the head of
exactly one of the edges
Tree terminology & properties
Height of a node in a tree
Length of the longest path from that node to
a leaf
Depth of a node in a tree
Length of the path from the root of the tree
to the node

Tree terminology
root
root
leaf
leaf
leaf
leaf
leaf
leaf
Tree terminology
u
v
A path from u to v. Node u is the ancestor of
node v.
Tree terminology
w has depth 2
u has height 3
tree has height 4

u
w
Special kinds of trees
Ordered v. unordered
Binary tree
Empty v non-empty
Full
Perfect
Complete
Ordered trees
Have a linear order on the children of
each node
That is, can clearly identify a 1
st
, 2
nd
, ,
k
th
child
An unordered tree doesnt have this
property
Binary tree
An ordered tree with at most two children
for each node
If node has two child, the 1
st
is called the
left child, the 2
nd
is called the right child
If only one child, it is either the right child
or the left child
Two trees are not
equivalent One has only
a left child; the other has
only a right child
Empty binary tree
Convenient to define an empty binary
tree, written A, for use in this definition:
A binary tree is either A or is a node with left
and right subtrees, each of which is a binary
tree.
Full binary tree
Has no nodes with only one child
Each node either a leaf or it has 2 children
# leaves = #non-leaves + 1

Perfect binary tree
A full binary tree in which all leaves have the
same depth
Perfect binary tree of height h has:
2
h+1
-1 nodes
2
h
leaves
2
h
-1 non-leaves
Interesting because it is fully packed, the
shallowest tree that can contain that many
nodes. Often, well be searching from the root.
Such a shallow tree gives us minimal number
of accesses to nodes in the tree.
Perfect binary trees
Complete binary tree
Perfect binary tree requires that number of
nodes be one less than a power of 2.
Complete binary tree is a close approximation,
with similar good properties for processing of
data stored in such a tree.
Complete binary tree of height h:
Perfect binary tree of height h-1
Add nodes from left at bottom level

More formally,
A complete binary tree of height 0 is any tree
consisting of a single node.
A complete binary tree of height 1 is a tree of
height 1 with either two children or a left child
only.
For height h >= 2, a complete binary tree of
height h is a root with two subtrees satisfying
one of these two conditions:
Either the left subtree is perfect of height h-1 and
the right is complete of height h-1 OR
The left is complete of height h-1 and the right is
perfect of height h-2.
Complete binary trees
Tree terminology
Forest
A finite set of trees
In the case of ordered trees, the trees in the
forest must have a distinguishable order as
well.

Tree Operations
Parent(v)
Children(v)
FirstChild(v)
RightSibling(v)
LeftSibling(v)
LeftChild(v)
RightChild(v)
isLeaf(v)
Depth(v)
Height(v)
Parent(v)
Return the parent of node v, or A if v is
the root.
Children(v)
Return the set of children of node v (the
empty set, if v is a leaf).
FirstChild(v)
Return the first child of node v, or A if v
is a leaf.
RightSibling(v)
Return the right sibling of v, or A if v is
the root or the rightmost child of its
parent.

LeftSibling(v)
Return the left sibling of v, or A if v is the
root or the leftmost child of its parent
LeftChild(v)
Return the left child of node v;
Return A if v has no left or right child.
RightChild(v)
Return the right child of node v;
return A if v has no right child


isLeaf(v)
Return true if node v is a leaf, false if v
has a child
Depth(v)
Return the depth of node v in the tree.


Height(v)
Return the height of node v in the tree.

Arithmetic Expression Tree
Binary tree associated
with an arithmetic
expression
internal nodes: operators
external nodes: operands
Example: arithmetic
expression tree for the
infix expression:
(2 (a 1) + (3 b))
Note: infix expressions
require parentheses
((2 x a) (1+3) * b) is a
different expression tree

Evaluating Expression Trees
function Evaluate(ptr P): integer
/* return value of expression represented by tree
with root P */
if isLeaf(P) return Label(P)
else
x
L
<- Evaluate(LeftChild(P))
x
R
<- Evaluate(RightChild(P))
op <- Label(P)
return ApplyOp(op, x
L
, x
R
)
Example
- Evaluate (A)
Evaluate(B)
Evaluate(D) => 2
Evaluate(E)
Evaluate(H)=> a
Evaluate(I) => 1
ApplyOp(-,a,1) => a-1
ApplyOp(x,2,(a-1)) => (2 x (a-1))
Evaluate(C)
Evaluate(F) => 3
Evaluate(G) => b
ApplyOp(x,3,b) => 3 x b
ApplyOp(+,(2 x (a-1)),(3 x b)) =>
(2 x (a-1)) + (3 x b)
A
B C
D
E
F G
H I
Traversals
Traversal = any well-defined ordering of the
visits to the nodes in a tree
PostOrder traversal
Node is considered after its children have been
considered
PreOrder traversal
Node is considered before its children have been
considered
InOrder traversal (for binary trees)
Consider left child, consider node, consider right child

PostOrder Traversal
procedure PostOrder(ptr P):
foreach child Q of P, in order, do
PostOrder(Q)
Visit(P)
PostOrder traversal gives:
2, a, 1, -, x, 3, b, x, +

Nice feature: unambiguous doesnt
need parentheses
-- often used with scientific calculators
-- simple stack-based evaluation
Evaluating PostOrder Expressions
procedure PostOrderEvaluate(e
1
, , e
n
):
for i from 1 to n do
if e
i
is a number, then push it on the stack
else
Pop the top two numbers from the stack
Apply the operator e
i
to them, with the right operand being the
first one popped
Push the result on the stack
PreOrder Traversal
procedure PreOrder(ptr P):
Visit(P)
foreach child Q of P, in order, do
PreOrder(Q)

PreOrder Traversal:
+, x, 2, -, a, 1, x, 3, b

Nice feature: unambigous, dont need
parentheses
-- Outline order
InOrder Traversal
procedure InOrder(ptr P):
// P is a pointer to the root of a binary tree
if P = A then return
else
InOrder(LeftChild(P))
Visit(P)
InOrder(RightChild(P))
InOrder Traversal:
2 x a 1 + 3 x b


Binary Search Trees
Use binary trees to store data that is
linearly ordered
A linear order is a relation < such that for
any x, y, and z
If x and y are different then either x<y or y<x
If x<y and y < z then x< z
An inorder traversal of a binary search
tree yields the labels on the nodes, in
linear order.
Binary Search Trees, examples

50
70
60
30
40 20
Inorder traversal yields: 20, 30, 40, 50, 60, 70

Level-order traversal
A
F
C
B
E D
breadth-first or level-order traversal yields:
A, B, C, D, E, F
a.k.a. breadth-first traversal
Tree Implementations
General Binary Tree
General Ordered Trees
Complete Binary Trees
Binary Trees:
Natural Representation
LC : contains pointer to
left child
RC: contains pointer to
right child
Info: contains info or
pointer to info
LeftChild(p),
RightChild(p): O(1)
Parent(p): not O(1)
unless we also add a
parent pointer to the
node
LC info RC
Parent(p): not O(1)
unless we also add a
parent pointer to the
node
Can use xor trick to
store parent-xor-left,
Parent-xor-right, and
use only two fields
Ordered Tree representation
Binary tree: array of 2 ptrs to 2 children
Ternary tree: array of 3 ptrs to 3 children
K-ary trees: k pointers to k children
What if there is no bound on the number
of children?
Use binary tree representation of ordered
tree:
First Info Right
Child Sibling
Example
General ordered tree Binary tree rep
A
B C
F
D
G I H E
I
H
G
F
E
D
C
B
A
Complete Binary Tree:
Implicit representation
Binary Search Trees, example
50
60
70
30
40 20
0 1 2 3 4 5
[50 | 30 | 70 | 20 | 40| 60]
Implicit rep of complete binary
trees
isLeaf(i) :
2i + 1 >= n

LeftChild(i) :
2i + 1 (none if 2i + 1 >= n)

RightChild(i) :
2i + 2 (none if 2i + 2 >= n)

LeftSibling(i):
i-1 (none if i=0 or i is odd)

RightSibling(i):
i+1 (none if i=n-1 or i is even)

Parent(i):
floor((i-1)/2) (none if i=0)

Depth(i):
floor (lg(i+1))

Height(i):
ceil(lg((n+1)/(i+1))) - 1

Tree Traversals and Scans
Stack-based Traversals
Link-Inversion Traversal
Scanning in Constant Space
Threaded Trees
Level-Order Traversal
Stack-based Traversals
procedure Traverse(ptr P)
//traverse binary tree with root P
if P != A then
PreVisit(P)
Traverse(LC(P))
InVisit(P)
Traverse(RC(P))
PostVisit(P)

Provide functions for
Previsit(P),Invisit(P),
and/or PostVisit(P)
Some can be null (do-
nothing) functions
Implements all 3
traversal (inorder,
preorder, postorder)
just depends on which
functions have real
bodies


Stack-based Traversals
procedure Traverse(ptr P)
//traverse binary tree with root P
if P != A then
PreVisit(P)
Traverse(LC(P))
InVisit(P)
Traverse(RC(P))
PostVisit(P)


O(n) time to visit all
nodes
Height of the stack is
proportional to the
height of the tree
If structure is large,
height of stack can be
problematic
For pre-order and
inorder, one of the calls
to Traverse is tail-
recursive & we can
eliminate it
Stack-based Traversals
procedure Traverse(ptr P)
//traverse binary tree with root P
if P != A then
PreVisit(P)
Traverse(LC(P))
InVisit(P)
Traverse(RC(P))
PostVisit(P)

procedure Traverse(ptr P)
//traverse binary tree with root P
While P != A do
PreVisit(P)
Traverse(LC(P))
InVisit(P)
P <- RC(P)


- demo difference in stack
heights for small
example

Stack during recursive
in-order traversal
D
F
A
E
C
B
D
B
A C
E
F
A
C
B C D E F
Height of stack for improved algorithm
How high does the
stack get for this
alg?
Depends on max
left height --
proportional to the
height of the
corresponding
ordered tree
OrdHt(P):
if P = A
0
if LC(P) == A
OrdHt(RC(P))
Otherwise
Max(1 + OrdHt(LC(P)),
OrdHt(RC(P)))

Example: calculate
ordered height for
medium-sized BT
Link-Inversion Traversal
Use same ideas
from link-inversion
traversal of list
Stores contents of
stack in tree itself
Use tag bit to
indicate whether to
follow LC or RC at
this point
tag LC info RC
Tag also indicates:
When recursive invocation
finishes
Which of the two recursive calls
in the body caused that
invocation
Where in the body the
execution should continue
Link Inversion Traversal
procedure LinkInvTrav(ptr Q)
// initially Q points to root
P <- A
repeat forever
// descend to left afap
while Q != A do
PreVisit(Q)
Tag(Q) <- 0
descend_to_left


// ascend from right afap
while P != A and Tag(P) == 1 do
ascend_from_right
PostVisit(Q)

if P == A then return
else
ascend_from_left
InVisit(Q)
Tag(Q) <- 1
descend_to_right


Ascend/ Descend functions
descend_to_left



ascend_from_left

descend_to_right



Ascend_from_right



P
Q
LC(Q
|
\



|
.
|
|
|

Q
LC(Q)
P
|
\



|
.
|
|
|

Q
P
LC(P)
|
\



|
.
|
|
|

P
LC(P)
Q
|
\



|
.
|
|
|

P
Q
RC(Q)
|
\



|
.
|
|
|

Q
RC(Q)
P
|
\



|
.
|
|
|

Q
P
RC(P)
|
\



|
.
|
|
|

P
RC(P)
Q
|
\



|
.
|
|
|
Example
Demo on small - midsized tree
One more twist
Actually, dont really need to store the
tag bits in the nodes themselves can
just place them in a stack at the time the
algorithm runs!
Scanning in Constant Space
Can visit each node in a tree, at least
once, using only a few temporary
variables
Catch: not a standard traversal order
In fact - visits each node three times
walks around the tree, keeping nodes
and edges to its left

Constant Space Scan
50, 30, 20, 20, 20, 30, 40, 40, 40, 30, 50, 70, 60, 60, 60, 70, 70, 50
50, 30, 20, 40, 70, 60
20 30 40 50 60 70
20 40 30 60 70 50

Trees, example
50
60
70
30
40 20
Constant Space Scan
procedure ConstantSpaceScan(ptr Q):
I is a distinguished value
Initially Q points to root
P <- I
While Q != I do
If Q != A then
Visit(Q)




else
P <-> Q

P
Q
LC(Q)
RC(Q)
|
\




|
.
|
|
|
|

Q
LC(Q)
RC(Q)
P
|
\




|
.
|
|
|
|
Threaded Trees
Inorder traversal is a common operation
Recursive methods have disadvantages:
Require extra memory to store a stack
Cant start from an arbitrary node need to
start at the root
Want to make it easier to find inorder
successor of a node
Inorder predecessor would be nice too!
Threaded Trees


50 30
60
20
10
40 200 | 40 | 300
A | 50 | 500
400 | 30 | A
A | 60| A A | 10 | 600
A | 20 | A
100
200
300
500 400
600
Hmmmm thats quite a few null pointers!
In fact, in binary tree with n nodes, there are 2n pointers .. And n+1
of these are A maybe we can make use of this memory .
Threaded Trees
If a node has no right child, store a
pointer to the nodes inorder successor
If a node has no left child, store a pointer
to the nodes inorder predecessor
Need to keep a threat bit to distinguish
between regular pointers and threads
Threaded Trees


50 30
60
20
10
40 200 | 40 | 300
A | 50 | 500
400 | 30 | A
A | 60| A A | 10 | 600
A | 20 | A
100
200
300
500 400
600
On board .. Sketch out threaded equivalent of this tree .
InOrder Successor
function InorderSuccessor(ptr N): ptr
//return the inorder successor of N or A if none exists
P <- RC(N)
if P = A then return A
else if P is not a thread then
while LC(P) is not a thread or A do
P <- LC(P)
return P
Threaded Insert
Procedure ThreadedInsert(ptr P,Q):
// Make node Q the inorder successor of node P





if RC(Q) is not a thread then
LC(InOrderSuccessor(Q)) <- (thread)Q


RC(P)
LC(Q)
RC(Q)
|
\



|
.
|
|
|

(child)Q
(thread)P
RC(P)
|
\



|
.
|
|
|
Threaded Trees
T h r e a d e d T r e e s


200 | 40 | 300
A | 50 | 500
400 | 30 | A
A | 60| A A | 10 | 600
A | 20 | A
100
200
300
500 400
600
Level-Order Traversal
Procedure LevelOrder(ptr R):
//R is a ptr to the root of the tree
L <- MakeEmptyQueue()
Enqueue(R, L)
Until IsEmptyQueue(L)
P <- Dequeue(L)
Visit(P)
Foreach child Q of P, in order, do
Enqueue(Q,L)

You might also like