500 likes | 766 Views
Tree Data Structures. Binary Tree Iterator. Similar idea to Linked Lists: Define a class that handles the traversal of the complete tree for us, allowing Start at root Movement to next node Visiting the data Arbitrary stopping (don’t have to completely traverse)
E N D
Binary Tree Iterator • Similar idea to Linked Lists: Define a class that handles the traversal of the complete tree for us, allowing • Start at root • Movement to next node • Visiting the data • Arbitrary stopping (don’t have to completely traverse) • Implement iteratively (not recursively) – to allow for easy arbitrary stopping • Iterative needs to emulate recursive traversal – use a Stack?
InorderIterator Class class InorderIterator { public: InorderIterator(BinaryTree& inputTree): tree(inputTree) { CurrentNode = tree.root;}; char* Next(); private: const BinaryTree& tree; Stack<BinaryTreeNode*> stack; BinaryTreeNode* currentNode; }
InorderIterator Class char *InorderIterator::Next() { while (currentNode) { stack.Add(currentNode); currentNode = currentNode->leftChild; } if (! stack.isEmpty()) { currentNode = *(stack.Delete(currentNode)); char& temp = &(currentNode->data); currentNode = currentNode->rightChild; return &temp; } else return 0; // traversed whole tree }
InorderIterator Validation + * E D * A / / / / C * * * * * * * * * A B + + + + + Stack: + Output: A
InorderIterator Validation + * E D * / B / C * * * * * * A B Stack: + + + Output: / A [ETC…]
InorderIterator Analysis • Computational complexity: • n nodes in tree • Every node is placed on stack once • As you pass it on your way down • Every node is removed from stack once • As you visit it on your way back up • O(n) computation • O(height) space -> bounded between: • O(log2n) on low end (complete, full) • O(n) on high end (skewed)
Level-Order Traversal Rules: • Instead of going down to children first, go across to siblings • Visits all nodes on a given level in left-to-right order 1 2 3 4 5 6 7 1 3 2 4 5 6 7
Level Order Traversal • Level order traversal also known as breadth-first traversal • Previous traversals made use of stack • Literally with iterative traversal • Figuratively with recursive traversal • To handle breadth-first search, need a queue in place of a stack
Level Order Traversal • Add root node to queue • For a given node from the queue • Visit node • Add nodes left child to queue • Add nodes right child to queue
Level Order Traversal void BinaryTree::LevelOrder() { Queue<BinaryTreeNode*> queue; BinaryTreeNode* currentNode = root; while (currentNode) { cout << currentNode->data; if (currentNode->leftChild) queue.Add(currentNode->leftChild); if (currentNode->rightChild) queue.Add(currentNode->rightChild; currentNode = *(queue.Delete(currentNode)); }
LevelOrderValidation + * E D * / C A B Queue: * E E * D * D D / C Output: E + * *
Binary Tree Operations • Extensions to Binary Tree class to support: • Copy Constructor (also very similar to = operation) • Equivalence (==) (only takes 1 more step to != implementation)
Binary Tree Operations • Copy constructor: • Goal: Create a tree that mirrors the current tree • Do a tree traversal, creating new nodes in new tree as encounter first time in current tree.
Binary Tree Copy Constructor BinaryTree::BinaryTree(const BinaryTree & input) { root = copy(input.root); } BinaryTreeNode* copy(BinaryTreeNode* current) { if (current) { BinaryTreeNode *temp = new BinaryTreeNode; temp->data = current->data; temp->leftChild = copy(current->leftChild); temp->rightChild = copy(current->rightChild); return temp; } else return 0; }
Binary Tree Equivalence • Operator==: • Goal: Determine whether or not the two trees have the same data values and link orderings • Do a tree traversal, • Check whether or not nodes in the same place • Compare data values
Binary Tree Equivalence bool operator==(const BinaryTree & tree1, const BinaryTree & tree2) { return equal(tree1.root, tree2.root); } bool equal(BinaryTreeNode* a, BinaryTreeNode* b) { if ((!a) && (!b)) return 1; // both null pointers if (a && b && (a->data == b->data) // same data && equal(a->leftChild,b->leftChild) // same left && equal(a->rightChild, b->rightChild) // same right return 1; return 0; }
Binary Trees as Tools:Exploiting Traversal • Satisifiability Problem: • Given an arbitrary boolean expression, determine if there is a set of assignments to the variables so that the expression is true. (!x1 && x2) => x1 = false, x2 = true Satisfying assignment
Satisfiability Rules • Expressions: • A variable is an expression • If x, y are expressions, so are • x && y, x || y, and !x • Normal order of evaluation: not > and > or • Parentheses can alter order of evaluation
Satisfiability Problem • Represent expression in Binary Tree (x1 && !x2) || (!x1 && x3) || !x3 || Note: Terminals are all operands Internal nodes are operators. ! operator only has right child (unary) ! || x3 && && ! ! x1 x3 x2 x1
Satisfiability Problem • Evaluate tree in postfix order (LRV) • Allows proper passing up of computed subexpression values • Computing left and right child (boolean values) before computing yourself, whose value is dependent on children. • Need to evaluate with all possible instantiations of true/false for terminal nodes
Satisfiability Problem • Update node definition: • Data – holds type (and, or, not, true, false) • Value – holds answer computed from children class SatNode { friend class SatTree; private: SatNode *leftChild; TypesOfData data; bool value; SatNode* rightChild; }
Satisfiability Problem • With each instantiation at the main level, will set node->data = initial true/false value for terminal nodes • Propagate up, without losing information on what types of operations are performed at higher levels
Satisfiability Problem • Evaluation function: Rewrite of postorder traversal function void SatTree::PostOrderEval() { PostOrderEval(root); } void SatTree::PostOrderEval(SatNode *s) { if (node) { PostOrderEval(s->leftChild); PostOrderEval(s->rightChild); switch (s->data) { case LogicalNot: s-> value = !(s->rightChild->value); break; case LogicalAnd: s->value = s->leftChild->value && s->rightChild->value; break; case LogicalOr: s->value = s->leftChild->value || s->rightChild->value; break; case LogicalTrue: s->value = 1; break; case LogicalFalse: s-value = 0; break; } } }
Review Questions A A B C B C D E D D C B A Inorder traversal? (LVR) D B E A C
Review Questions A A B C B C D E D A B C D Preorder traversal? (VLR) A B D E C
Review Questions A A B C B C D E D D C B A Postorder traversal? (LRV) D E B C A
Review Questions • Write a function to compute the number of leaf nodes in a binary tree. int BinaryTree::CountTerminals() { return CountTerminals(root); } int BinaryTree::CountTerminals(BinaryTreeNode* node) { int left = 0; int right = 0; if ((node->leftChild == 0) && (node->rightChild == 0)) return 1; else { if (node->leftChild != 0) left = CountTerminals(node->leftChild); if (node->rightChild != 0) right = CountTerminals(node->rightChild); return left + right; } }
Priority Queues • Standard queue definition: • Add to back, remove from front • Priority queue definition: • Add arbitrary priority to back, remove highest priority item • Very common! • OS scheduling • Packet switching
Priority Queues – OS Job Scheduling CPU Next Job 5 3 7 12 15 turketwh ls turketwh g++ thomas df -k turketwh gnome root backup
Priority Queues • Other Examples: • Selling service of a machine (renting carpet cleaners?) • Goal maximize number of users in a fixed time • Allow smallest homes (shortest rental time) to go first (a min priority queue) • Goal maximize money in a fixed time • Allows homeowners willing to pay the most to go first (a max priority queue)
Priority Queue Interface Definition: template <class Type> class MaxPriorityQueue { public: virtual void Insert(const Element<Type> & toInsert) = 0; virtual Element<Type>* Delete (Element<Type> &) = 0; //(DeleteMax or DeleteMin) }
Priority Queue Implementations • List implementations (on linked list): • Unordered list: • Insert – attach at front of list: O(1) • Delete – search entire list: O(N) • Sorted list: • Insert – find right place to insert: O(N) • Delete – pull off front: O(1)
Heaps • Heaps are a guaranteed fast way to implement priority queues • Two Heap definitions: • A max tree is a tree in which the key value in each node is no smaller than the key values in its children (if any). A max heap is a complete binary tree that is also a max tree. • In a min tree, node keys are no larger than those for a node’s children. Min heap is a complete binary tree that is also min tree. • Root is largest item in max tree, smallest item in min tree
Heap Examples: • Max Heaps: 9 9 14 6 6 7 3 3 12 5 10 8 6 5 Not A Max Heap! Complete binary? Yes Max Tree Property? Yes Complete binary? Yes Max Tree Property? Yes Complete binary? No Max Tree Property? Yes
Heap Examples: • Min Heaps: 21 10 2 11 20 4 83 7 50 10 8 6 Not A Min Heap! Complete binary? Yes Min Tree Property? Yes Complete binary? Yes Min Tree Property? Yes Complete binary? Yes Min Tree Property? No
Max Heap Operations • Minimal Interface: • Create an empty heap • Insert new element into heap • Delete largest element from the heap Same functionality as priority queue!
Max Heap Definition: Variables • Since heaps are complete binary trees, simplest and densest implementation is via an array • Could also implement with left, right pointers. class MaxHeap { public: private: Element<Type> *heap; int currentSize; int maxSize; }
Max Heap Definition: Methods class MaxHeap { public: MaxHeap(int size); bool isFull(); bool isEmpty(); void insert(Element<KeyType> toInsert); Element<KeyType>* delete(KeyType& toDelete); private: // see previous slide }
Max Heap Implementation Constructor template<class KeyType> MaxHeap<KeyType>::MaxHeap(int size) { maxSize = size; currentSize = 0; heap = new Element<KeyType>[maxSize+1]; // heap[0] not used – simplifies binary tree // mapping } isFull, isEmpty() simple one-line conditional checks currentSize == maxSize or currentSize == 0
Max Heap: Insertion To Insert First Rule: Insert in correct place to preserve complete binary tree Second Rule: Ensure Max Tree property 20 1 2 15 14 10 1
Max Heap: Insertion To Insert First Rule: Insert in correct place to preserve complete binary tree Second Rule: Ensure Max Tree Property Repeatedly swap with parent if larger 20 5 2 15 14 10 20 5 5 15 14 If inserting 25 instead of 5, would have to move all the way to root 10 2
Max Heap: Insertion template <class KeyType> Void MaxHeap<KeyType>::Insert(const Element<Type> & toInsert) { if (isFull()) return; currentSize = currentSize + 1; for (int i = currentSize; true; ) { if (i == 1) break; // if at root stop if (toInsert.key < heap[i/2].key) break; // if smaller than parent stop (preserve max tree property) heap[i] = heap[i/2]; // swap with parent i = i /2; // set parent position as potential insert // location } heap[i] = toInsert; // insert at appropriate place }
Max Heap: Insertion Big Oh Analysis • Insertion: • Start Location: Leaf node (bottom of tree) • End Location: Root maximally, often less than root • Max distance between root and leaf node is log2(n+1) • O(1) at each node (simple compare) • O(log n) total time [better than sorted list implementation]
Max Heap: Deletion 2 First Rule: Remove root node – Guaranteed largest Second Rule: Preserve complete binary tree property - Move rightmost, lowest into root position Third Rule: Preserve max tree property- Repeatedly push root down, swapping with larger of two children 20 5 5 15 15 14 14 10 10 2 15 5 14 2 10
Max Heap: Deletion Implementation template <class KeyType> Element<KeyType>* MaxHeap<KeyType>::DeleteMax(Element <KeyType> & x) { if (isEmpty()) return 0; x = heap[1]; // grab root to return Element<KeyType> k = heap[currentSize]; // grab lowest rightmost item currentSize = currentSize – 1; for (int i= 1, j = 2; j <= currentSize; ) { if (j < currentSize) if (heap[j].key < heap[j+1].key) j++; // j points to the larger child now if (k.key >= heap[j].key) break; // in right place already heap[i] = heap[j]; // move child up if not in right place i = j; j *= 2; // move i,j down to continue swapping } heap[i] = k; // put in right place return &x; }
Max Heap: Deletion Big O Analysis • 1 step to remove root node • 1 step to move bottom, right node to root • Potentially could move all the way down the tree in preserving max tree property • Max height = log2(n+1) • Each move is O(1) – a comparison • Overall work is O(log2n)
Priority Queue Big Oh Comparisons: • Max Heap implementation of priority queue: • O(log n) insert O(log n) delete • Unsorted array • O(1) insert O(n) delete • Sorted array • O(n) insert O(1) delete