Binary Search Tree and AVL More Trees, More Solutions Binary Search Trees Binary Tree Terms a a b d h c e b f g m n c d o A binary tree is balanced if the left and right subtrees' heights differ by at most one. So the left subtree must be balanced and the right subtree must be balanced. h This binary tree is unbalanced. Binary Search Binary search can perform search operations on an array-based sequence that are sorted by key At each step, the number of candidate items is halved This means that the algorithm terminates after O(log n) steps, as shown in find(7) int binarySearch(int array[], int left, int right, int target){ if (right >= left) { int middle = left + ((right - left) / 2); Recursive Function // target is the middle element if (array[middle] == target) return middle; // target is smaller than middle, it’s in left subarray if (array[middle] > target) { return binarySearch(array, left, middle - 1, target); } Binary Search // target is larger than middle, it’s in right subarray return binarySearch(array, middle + 1, right, target); } // target is not in the array return -1; } Binary Search Trees A binary search tree is a binary tree storing items, keys (or key-value entries) at its internal nodes and satisfying the following property: ● ● ● ● u, v, and w are nodes u is in the left subtree of v w is in the right subtree of v key(u) ≤ key(v) ≤ key(w) An inorder traversal of a binary search trees visits the keys in increasing order Binary Search Trees External Nodes (Leaves) The book believes external nodes do not store items because they serve as “placeholders.” This approach simplifies their search/update algorithms. However, this is not always the case. For this presentation, we will follow the book’s method Just keep this in mind as you read material outside of this book VisuAlgo - Binary Search Tree // a binary search tree Interface (pt. 1) template <typename E> class SearchTree { public: typedef typename E::Key K; Binary Search Tree // a key (from Entry in Maps) typedef typename E::Value V; // a value (from Entry in Maps) class Iterator; // an iterator/position // public functions public: Interface (pt. 2) SearchTree(); // constructor int size() const; // number of entries bool empty() const; // is the tree empty? Iterator find(const K& k); // find entry with key k Iterator insert(const K& k, const V& x); // insert (k,x) // remove key k entry Binary Search Tree void erase(const K& k) throw(NonexistentElement); // remove entry at p void erase(const Iterator& p); Iterator begin(); // iterator to first entry Iterator end(); // iterator to end entry // local utilities protected: // linked binary tree typedef BinaryTree<E> BinaryTree; Interface (pt. 3) // position in the tree typedef typename BinaryTree::Position TPos; TPos root() const; Binary Search Tree // get virtual root TPos finder(const K& k, const TPos& v); // find utility TPos inserter(const K& k, const V& x); // insert utility TPos eraser(TPos& v); // erase utility TPos restructure(const TPos& v) // restructure throw(BoundaryViolation); private: // member data BinaryTree T; // the binary tree int n; // number of entries public: class Iterator { // an iterator/position private: TPos v; // which entry Interface (pt. 4) public: Iterator(const TPos& vv) : v(vv) { } // constructor // get entry (read only) const E& operator*() const { return *v; } // get entry (read/write) Binary Search Tree E& operator*() { return *v; } // are iterators equal? bool operator==(const Iterator& p) const { return v == p.v;} }; }; Iterator& operator++(); // inorder successor friend class SearchTree; // give search tree access Question: What is the successor (next node inorder) for nodes 44 and 29? Come up with an algorithm for both cases. Does either case have any nodes for which it wouldn’t work? Answer: 44: if there is a right child, go once to the right and all the way to the left. 29: if there is no right child, go up to the left until you can no longer, then once up to the right. Last node has no successor. template <typename E> // inorder successor typename SearchTree<E>::Iterator& SearchTree<E>::Iterator::operator++() { TPos w = v.right(); if (w.isInternal()) { do { // have right subtree? // move down left chain v = w; w = w.left(); } while (w.isInternal()); Iterator ++ } else { w = v.parent(); // get parent while (v == w.right()) { // move up right chain Binary Search Tree v = w; w = w.parent(); } // and first link to left v = w; } return *this; } Binary Search Trees The increment operator contains an obvious bug If the iterator points to the rightmost node of the entire tree, then the above function would loop until arriving at the root, which has no parent. The rightmost node of the tree has no successor, so the iterator should return the value end. So, a special sentinel node is added to our tree, called the super root, which is created when the initial tree is constructed. The root of the binary search tree, which we call the virtual root, is made the left child of the super root. We define end to be an iterator that returns the position of the super root // constructor (create the super root) template <typename E> SearchTree<E>::SearchTree() : T(), n(0) { T.addRoot(); T.expandExternal(T.root()); } // get virtual root (left child of super root) Constructor Binary Search Tree template <typename E> typename SearchTree<E>::TPos SearchTree<E>::root() const { return T.root().left(); } // REMINDER CODE: expand external node void LinkedBinaryTree::expandExternal(const Position& p) { Node* v = p.v; // p's node v->left = new Node; // add a new left child v->left->par = v; // v is its parent v->right = new Node; // and a new right child v->right->par = v; // v is its parent n += 2; // two more nodes } void LinkedBinaryTree::addRoot() { _root = new Node; n = 1; } Begin and End Methods template <typename E> // iterator to first entry typename SearchTree<E>::Iterator SearchTree<E>::begin() { TPos v = root(); // start at virtual root while (v.isInternal()) v = v.left(); // find leftmost node return Iterator(v.parent()); } // iterator to end entry Binary Search Tree template <typename E> typename SearchTree<E>::Iterator SearchTree<E>::end() { return Iterator(T.root()); // return the super root } Binary Search Trees To search for a key k, we trace a downward path starting at the root The next node visited depends on the comparison of k with the key of the current node If we reach a leaf, the key is not found Example: find(4) // find utility template <typename E> typename SearchTree<E>::TPos SearchTree<E>::finder(const K& k, const TPos& v) { if (v.isExternal()) return v; // key not found if (k < v->key()) return finder(k, v.left()); Find Binary Search Tree // search left subtree else if (v->key() < k) return finder(k, v.right()); // search right subtree else return v; // found it here } // find entry with key k template <typename E> typename SearchTree<E>::Iterator SearchTree<E>::find(const K& k) { TPos v = finder(k, root()); // search from virtual root if (v.isInternal()) return Iterator(v); // found it else return end(); } // didn't find it Binary Search Trees To perform operation put(k, o), we search for key k (using TreeSearch) Assume k is not already in the tree, and let w be the leaf reached by the search We insert k at node w and expand w into an internal node Example: insert(5) // insert utility template <typename E> typename SearchTree<E>::TPos SearchTree<E>::inserter(const K& k, const V& x) { TPos v = finder(k, root()); // search from virtual root while (v.isInternal()) // key already exists? v = finder(k, v.right()); Insert Binary Search Tree // look further T.expandExternal(v); // add new internal node v->setKey(k); // set entry v->setValue(x); n++; // one more entry return v; // return insert position } // insert (k,x) template <typename E> typename SearchTree<E>::Iterator SearchTree<E>::insert(const K& k, const V& x) { TPos v = inserter(k, x); return Iterator(v); } Binary Search Trees To perform operation erase(k), we search for key k Assume key k is in the tree, and let let v be the node storing k If node v has a leaf child w, we remove v and w from the tree with operation removeAboveExternal(w), which removes w and its parent Example: remove(4) Binary Search Trees Consider the case where the key k to be removed is stored at a node v whose children are both internal Find the internal node w that follows v in an inorder traversal (successor) Copy key(w) into node v Remove node w and its left child z (which must be a leaf) by means of operation removeAboveExternal(z) Example: erase(3) // remove key k entry template <typename E> void SearchTree<E>::erase(const K& k) throw(NonexistentElement) { Erase (Pt. 1) TPos v = finder(k, root()); // search from virtual root if (v.isExternal()) // not found? throw NonexistentElement("Erase of nonexistent"); eraser(v); // remove it } Binary Search Tree // erase entry at p template <typename E> void SearchTree<E>::erase(const Iterator& p) { eraser(p.v); } // remove utility template <typename E> typename SearchTree<E>::TPos SearchTree<E>::eraser(TPos& v) { TPos w; if (v.left().isExternal()) { w = v.left(); Erase (Pt. 2, Utility) // remove from left } else if (v.right().isExternal()) { w = v.right(); } else { w = v.right(); // remove from right // both internal? // go to right subtree do { w = w.left(); } while (w.isInternal()); // get leftmost node TPos u = w.parent(); Binary Search Tree // copy w's parent to v v->setKey(u->key()); v->setValue(u->value()); } // one less entry and remove w and parent n--; return T.removeAboveExternal(w); } Runtime of BST Methods? Operation size, empty Time O(1) find, insert, erase O(h) Want to prevent worst-case scenario: AVL Trees AVL Tree Definition AVL trees are balanced An AVL Tree is a binary search tree such that for every internal node v of T, the heights of the children of v can differ by at most 1 Named after inventors AdelsonVelsky and Landis AVL Tree Restructure (Case A, Single Rotation) Algorithm restructure(x): Input: node x of a BST T that has both a parent y and a grandparent z Output: Tree T after a trinode restructuring (which corresponds to a single or double rotation) involving nodes x, y, and z 1: Let (a, b, c) be a left-to-right (inorder) listing of the nodes x, y, and z, and let (T0, T1, T2, T3) be a left-to-right (inorder) listing of the four subtrees of x, y, and z not rooted at x, y, or z. 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b and let T0 and T1 be the left and right subtrees of a, respectively. 4: Let c be the right child of b and let T2 and T3 be the left and right subtrees of c, respectively. AVL Tree Restructure (Case A, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees 1 3 2 4 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. AVL Tree Restructure (Case A, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z a=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. c=x T0 T1 T2 T3 AVL Tree Restructure (Case A, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z a=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. c=x T0 T1 T2 T3 AVL Tree Restructure (Case A, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. c=x a=z T0 T1 T2 T3 AVL Tree Restructure (Case A, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. c=x a=z T0 T1 T2 T3 AVL Tree Restructure (Case A, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. c=x a=z T0 T1 T2 T3 AVL Tree Restructure (Case B, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z c=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. T3 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. a=x T2 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 AVL Tree Restructure (Case B, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z c=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. T3 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. a=x T2 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 AVL Tree Restructure (Case B, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z c=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. T3 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. a=x T2 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 AVL Tree Restructure (Case B, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. c=z 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. a=x 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 T2 T3 AVL Tree Restructure (Case B, Single Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=y 2: Replace the subtree rooted at z with a new subtree rooted at b. c=z 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. a=x 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 T2 T3 AVL Tree Restructure (Case C, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z a=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees c=y 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 b=x T3 T1 T2 AVL Tree Restructure (Case C, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees a=z c=y 2: Replace the subtree rooted at z with a new subtree rooted at b. T0 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. b=x 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T1 T2 T3 AVL Tree Restructure (Case C, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z c=y 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=x 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. a=z 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 T2 T3 AVL Tree Restructure (Case C, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=x 2: Replace the subtree rooted at z with a new subtree rooted at b. a=z c=y 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 T2 T3 AVL Tree Restructure (Case C, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=x 2: Replace the subtree rooted at z with a new subtree rooted at b. a=z c=y 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 T2 T3 AVL Tree Restructure (Case D, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z c=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees a=y 2: Replace the subtree rooted at z with a new subtree rooted at b. T3 b=x 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 T2 AVL Tree Restructure (Case D, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z c=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees a=y T3 2: Replace the subtree rooted at z with a new subtree rooted at b. 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. b=x T0 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T1 T2 AVL Tree Restructure (Case D, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z c=z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=x T3 2: Replace the subtree rooted at z with a new subtree rooted at b. a=y 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 T1 T2 AVL Tree Restructure (Case D, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=x 2: Replace the subtree rooted at z with a new subtree rooted at b. a=y 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 c=z T1 T2 T3 AVL Tree Restructure (Case D, Double Rotation) Input: node x of a BST T that has both a parent y and a grandparent z 1: a = z, b = y, c = x (left-to-right inorder) T0, T1, T2, T3 are the left-to-right inorder listing of the four subtrees b=x 2: Replace the subtree rooted at z with a new subtree rooted at b. a=y 3: Let a be the left child of b Let T0 and T1 be the left and right subtrees of a. 4: Let c be the right child of b Let T2 and T3 be the left and right subtrees of c. T0 c=z T1 T2 T3 AVL Tree Insertion Insertion is as in a binary search tree Always done by expanding an external node. Example: Inserting 54 (Case D, Double Rotation) AVL Tree Removal Removal begins as in a binary search tree, which means the node removed will become an empty external node. Its parent, w, may cause an imbalance. Example: Remove 32 (Case A, Single Rotation) Question: How many different binary search trees can store the keys {1, 2, 3}? Answer: 5 different BST. ● 2 with 1 as root, ● 1 with 2 as root, ● 2 with 3 as root