24. Trees and traversals
Rooted Tree
Property: there is exactly one path (no more, no less) between any two nodes of the tree.
- leaf
- internal node (the root is not necessarily an internal node)
- ancestors (technically including itself)
- descendant
- length = #edges
- depth = length of the path from n to the root (depth of root is zero)
- height = length of the path from n to its deepest descendant (height of leaf node is zero)
- height of the tree = depth of its deepest node = height of the root
- subtree
- binary tree, either left or right child
Representing Rooted Trees
G&T: each node has three references, an item, its parent and a list of its children.
SibTree
class SibTreeNode { | class SibTree {
Object item; | SibTreeNode root;
SibTreeNode parent; | int size;
SibTreeNode firstChild; | }
SibTreeNode nextSibling; |
} |
_______~jrs/61b_______ <-- Root node
/ | | \
/ | | \
hw index.html lab _lec__
/ \ /\ / /\ \ \_
/ \ ^ / \ / / \ \ \
hw1 hw2 | lab1 lab2 01 02 03 04 05 <-- Leaf nodes
Leaf node
===============================================================================
+ ROOTED TREE | -------------------- ---------------------------- +
=============== |--- ---- | | parent | +
+ ||.|root size|14| | ---------------------------- +
+ |-+- ---- | | item | +
+ --|----------------- ---------------------------- +
+ v SibTree object | firstChild | nextSibling | +
+ ----- ---------------------------- +
+ | * | structure of SibTreeNodes +
+ ----- +
+ Root node => |jrs| +
+ -----<--------- +
+ |.|*| \ +
+ /----<---- \ +
+ / ^^ \ \ +
+ v / \ \ \ +
+ ---/- -\--- -\--- -\--- +
+ | . | | . | | . | | . | +
+ ----- ----- ----- ----- +
+ |hw | |ind| |lab| |lec|<------------------------ +
+ ----- ----- ----- -----<------------------ \ +
+ |.|.+->|*|.+->|.|.+->|.|*|<------------ \ \ +
+ /---- ----- /---- --\--<------ \ \ \ +
+ / ^^ / ^^ \ ^ \ \ \ \ +
+ v / \ v / \ \ \ \ \ \ \ +
+ ---/- -\--- ---/- -\--- >-\--- -\--- -\--- -\--- -\--- +
+ | . | | . | | . | | . | | . | | . | | . | | . | | . | +
+ ----- ----- ----- ----- ----- ----- ----- ----- ----- +
+ |hw1| |hw2| |lb1| |lb2| |01 | |02 | |03 | |04 | |05 | +
+ ----- ----- ----- ----- ----- ----- ----- ----- ----- +
+ |*|.+->|*|*| |*|.+->|*|*| |*|.+->|*|.+->|*|.+->|*|.+->|*|*| +
+ ----- ----- ----- ----- ----- ----- ----- ----- ----- +
===============================================================================
Tree Traversals
- Preorder traversal: visit each node before recursively visiting its children.
- Postorder traversal: visit each node's children before the node itself.
- Inorder traversal for binary tree: left subtree, the root itself and then right subtree.
class SibTreeNode {
public void preorder() {
this.visit();
if (firstChild != null) {
firstChild.preorder();
}
if (nextSibling != null) {
nextSibling.preorder();
}
}
public void postorder() {
if (firstChild != null) {
firstChild.postorder();
}
this.visit();
if (nextSibling != null) {
nextSibling.postorder();
}
}
}
+
/ \ Prefix: + * 3 7 ^ 4 2
/ \
* ^ Infix: 3 * 7 + 4 ^ 2
/ \ / \
3 7 4 2 Postfix: 3 7 * 4 2 ^ +
- level-order traversal: you visit the root, then all the depth-1 nodes (from left to right), then all the depth-2 nodes, et cetera.
Unlike the three previous traversals, a level-order traversal is not straightforward to define recursively. However, a level-order traversal can be done in O(n) time.
Use a queue, which initially contains only the root. Then repeat the following steps:
- Dequeue a node.
- Visit it.
- Enqueue its children (in order from left to right). Continue until the queue is empty.
A final thought: if you use a stack instead of a queue, and push each node's children in reverse order--from right to left (so they pop off the stack in order from left to right)--you perform a preorder traversal. Think about why.