Lecture Notes 11: More trees 3/6
Correspond to arithmetic expression. The label in an internal node is
an operator, which operates on the expressions in the left and right
subtrees. The label on a leaf is a number. To simplify programming, we
put that in a separate field called value.
Evaluate a tree N:
- If N is a leaf, then its value.
- If N is an internal node, then recursively evaluate the left and
right subtrees and apply the operator at N to the two values.
Recursive tree traversal
Preorder search using a stack: No additional state
Preorder/postorder search using a stack: Additional state
Note: This is exactly how the run-time system handles function calls. It
uses a stack of records. Each record holds the state of an active function
call (values of local variables and place within the body of the function.)
Advantage to recursive method: The runtime system takes care of your stack
for you. Elegant code. Use the recursive method unless there is a
compelling reason not to.
Advantages to explicit stack:
- You can control the information being saved in the record. In some
cases you can achieve substantial space savings.
- Explicit control of the search. For instance suppose you want to have
two searches simultaneously working on the same tree, at different "speeds".
Each can work off its own stack. (There are more complex programming language
constructs e.g. threads that allow you to do this as well, but those have
their own overhead.)
- By generalizing the stack to other data structures you get other
forms of search. E.g. if you change the stack to a FIFO stack, you get
breadth-first search (below).
Tree traversal: Resource requirements
Time requirements: Proportional to number of nodes in tree. Overall in the
course of the entire search, every downward arc is examined once.
Stack method: Push a record every time you go down a level and pop it every
time you go up. So the maximum number of records in the stack is the neight of
Recursive method: Same thing. The stack is being manipulated behind the scenes
by the run-time system, but it's still there.
Start at the root; then go across the first row from left to right; then
across the second row from left to right; and so on.
Mammal, Rodent, Carnivore, Primate, Squirrel, Rat, Cat, Dog, Skunk, Gorilla,
Take the stack-based code for depth-first search.
- Change the stack to a FIFO queue.
- Add the children of c in forward order rather than backward
Initialize a FIFO queue Q to hold the root;
N = pop the first node off Q;
add N's children to the end of Q;
} until (Q is empty)
Unlike depth-first search, that's pretty much the only way to implement it.
In particular, there's nothing reasonable that corresponds to the recursive
implementation of DFS.
(You might think, "The recursive implementation of DFS is actually using a
stack of activation records, so it's just hiding the stack. Maybe if you
use a FIFO queue of activation records, you could get the same kind of
thing for BFS?" But it turns out that it's hard to make sense of a programming
languages that uses a FIFO queue of activation records.)