|
COT 5405 Advanced Algorithms Chris Lacher Notes 8: OUSet |
BinaryTree and Friends
- Node
class Node { T data_; Node *parent_, *leftChild_, *rightChild_; Node (T t) : data_(t), parent_(0), leftChild_(0), rightChild_(0) {} };BinaryTree class BinaryTree { Node * root_; public: Navigator Root(); ... };Navigator class Navigator { Node * currentNode_; public: Navigator& operator ++ (); Navigator& operator ++ (int); Navigator& operator -- (); ... }; ++nav; // go to left child nav++; // go to right child --nav; // go to parentIterator class BinaryTreeIterator { Navigator n_; public: // bidirectional iterator interface };
Iterator Algorithms
- BinaryTree::Begin()
{ Iterator i; i.Initialize(*this); return i; }BinaryTree::Iterator::Initialize(BinaryTree b) { // start at root n_ = b.Root(); // then slide left to lowest lchild while (n_.HasLeftChild()) ++n_; }BinaryTree::Iterator operator++() { if (!n_.Valid()) { return *this; } // now we have a valid navigator if (n_.HasRightChild()) // slide down the left subtree of right child { n_++; while (n_.HasLeftChild()) ++n_; } else // back up to first ancestor not already visited // as long as we are parent's right child, then parent has been visited { int wasRightChild; do { wasRightChild = n_.IsRightChild(); --n_; } while (wasRightChild); } return *this; }Analysis of Iterator Operations
Claim: For a BinaryTree b, the traversal loop
for (i = b.Begin(); i != b.End(); ++i){}has runtime Θ(n), where n is the number of nodes in the tree.
Proof: First note that all Navigator operations are constant time operations. Choose for our "atomic" a call to one of the Navigator motion operations ++n, n++, or --n. We claim that there are exactly 2n atomic computations in the traversal loop:
The traversal crosses each edge exactly twice: once descending with a call to ++n or n++ and once ascending with a call to --n, for a total count of 2e atomics, where e = number of edges of b. There is also one initial move (call to Begin()) to get on the tree and one final move to get off the tree (the last call to ++i). Therefore the traversal loop uses exactly 2e + 2 atomics. Since e + 1 = n, 2e + 2 = 2n, and we have proved the claim.
Corollary: Operator ++() has constant amortized runtime.
Remark: What this analysis shows is that the traversal loop controll structure has asymptotic runtime as small as possible, the same as a simple loop involving integer loop control variables. The cost of a client program loop will of course depend on what the client programmer does in the loop body.
BinarySearchTree
- BinaryTree + Total Ordering of Nodes
- Inherits Navigator, Iterator from BinaryTree
- Search Algorithm
Includes(t) { Navigator n_ = b.Root(); while (n_.Valid()) { if (t.key < n_.key) ++n_; else if (n_.key < t.key) n_++; else // t.key == n_.key return n_; } return n_; // null navigator }Runtime: O(depth)Insert Remove
OUSet < BinarySearchTree >
- Straightforward Adaptation
OUSet::Iterator < BinarySearchTree::Iterator >
- Iterator Interface: Bidirectional Iterator
- Adaptation
Maintaining Bushy Binary Search Trees (height = Θ(log size))
- Red Black Trees
- Height Balanced Trees
- Splay Trees
- Animation