# Basic Algorithms

================ Start Lecture #8 ================

Our medium term goal is to learn about tree traversals (how to "visit" each node of a tree once) and to analyze their complexity.

#### Complexity of Primitive Operations

Our complexity analysis will proceed in a somewhat unusual order. Instead of starting with the bottom or lowest level routines (the tree methods in 2.3.1, e.g., is Internal(v)) or the top level routines (the traversals themselves), we will begin by analyzing some middle level procedures assuming the complexities of the low level are as we assert them to be. Then we will analyze the traversals using the middle level routines and finally we will give data structures for trees that achieve our assumed complexity for the low level.

Let's begin!

#### Complexity Assumptions for the Tree ADT

These assumptions will be verified later.

• root(), parent(v), isInternal(v), isLeaf(v), isRoot(v), swapElements(v,w), replaceElement(v,e) each take Θ(1) time.
• The methods returning iterators, namely children(v), elements(), and positions(), each take time Θ(k), where k is the number of items being iterated over. k=#children for the first method and #nodes for the other two.
• For each iterator, the methods hasNext() and nextObject() take Θ(1) time. nextObject() sometimes has other names like nextPosition() or nextNode() or nextChild().

#### Middle level routines depth and height

Definitions of depth and height.

• The depth of the root is 0.
• The height of a leaf is 0.
• The depth of a non-root v is 1 plus the depth of parent(v).
• The height of an internal node v is 1 plus the maximum height of the children of v.
• The height of a tree is the height of its root.

Remark: Even our definitions are recursive!

From the recursive definition of depth, the recursive algorithm for its computation essentially writes itself.

Algorithm depth(T,v)
if T.isRoot(v) then
return 0
else
return 1 + depth(T,T.parent(v))

The complexity is Θ(the answer), i.e. Θ(dv), where dv is the depth of v in the tree T.

Problem Set #1, Problem 4:
Rewrite depth(T,v) without using recursion.
This is quite easy. I include it in the problem set to ensure that you get practice understanding recursive definitions.
The problem set is now assigned. It is due in 3 lectures from now (i.e., about 1.5 weeks).

The following algorithm computes the height of a position in a tree.

Algorithm height(T,v):
if T.isLeaf(v) then
return 0
else
h←0
for each w in T.children(v) do
h←max(h,height(T,w))
return h+1

Remarks on the above algorithm

1. The loop could (perhaps should) be written in pure iterator style. Note that T.children(v) is an iterator.
2. This algorithm is not so easy to convert to non-recursive form
Why?
It is not tail-recursive, i.e. the recursive invocation is not just at the end.
3. To get the height of the tree, execute height(T,T.root())
Algorithm height(T)
height(T,T.root())

Let's use the "official" iterator style.

Algorithm height(T,v):
if T.isLeaf then
return 0
else
h←0
childrenOfV←T.children(v)    // "official" iterator style
while childrenOfV.hasNext()
h&lar;max(h,height(T,childrenOfV.nextObject())
return h+1

But the children iterator is defined to return the empty set for a leaf so we don't need the special case

Algorithm height(T,v):
h←0
childrenOfV←T.children(v)    // "official" iterator style
while childrenOfV.hasNext()
h&lar;max(h,height(T,childrenOfV.nextObject())
return h+1

Theorem: Let T be a tree with n nodes and let cv be the number of children of node v. The sum of cv over all nodes of the tree is n-1.

Proof: This is trivial! ... once you figure out what it is saying. The sum gives the total number of children in a tree. But this almost all nodes. Indeed, there is just one exception.
What is the exception?
The root.

Corollary: Computing the height of an n-node tree has time complexity Θ(n).

Proof: Look at the code of the first version.

• Since each node calls height on each of its children, height is called recursively for every node that is a child, i.e. all but the root. The root is called directly from the top level.
• Hence height(T,v) is called once for each node v.
• "Most" of the code in height is clearly Θ(1) per call, or Θ(n) in total. The exception is the loop.
• Each iteration of the loop is Θ(1).
• The number of iterations in the invocation height(T,v) is the number of children in v.
• Hence the total number of iterations is the total number of children, which by the theorem is n-1.
• Hence the total cost of all the iterations is Θ(n).
• Hence the total cost of height(T) is the cost of "most" of the code plus the cost of the loops, which is Θ(N)+Θ(N), i.e. Θ(N).

To be more formal, we should look at the "official" iterator version. The only real difference is that in the official version, we are charged for creating the iterator. But the charge is the number of elements in the iterator, i.e., the number of children this node has. So the sum of all the charges for creating iterators will be the sum of the number of children each node has, which is the total number of children, which is n-1, which is (another) \$Theta;(n) and hence doesn't change the final answer.

Do a few on the board. As mentioned above, becoming facile with recursion is vital for tree analyses.

Definition: A traversal is a systematic way of "visiting" every node in a tree.

#### Preorder Traversal

Visit the root and then recursively traverse each child. More formally we first give the procedure for a preorder traversal starting at any node and then define a preorder traversal of the entire tree as a preorder traversal of the root.

Algorithm preorder(T,v):
visit node v
for each child c of v
preorder(T,c)

Algorithm preorder(T):
preorder(T,T.root())

Remarks:

1. In a preorder traversal, parents come before children (which is as it should be :-)).
2. If you describe a book as an ordered tree, with nodes for each chapter, section, etc., then the pre-order traversal visits the nodes in the order they would appear in a table of contents.

Do a few on the board. As mentioned above, becoming facile with recursion is vital for tree analyses.

Theorem: Preorder traversal of a tree with n nodes has complexity Θ(n).

Proof: Just like height.
The nonrecursive part of each invocation takes O(1+cv)
There are n invocations and the sum of the c's is n-1.

Homework: R-2.3