Question
In the AVLTree class the method doubleWithLeftChild performs a double rotation for case 2 and the method doubleWithRightChild performs a double rotation for case 3. Instead of using separate methods to perform the double rotation for ease 2 and 3, the AVLTree class can use just one method, called doubleRotation. The method doubleRotation has three parameters k1, k2, k3, each pointing to the correct nodes when calling doubleRotation for case 2 and when calling doubleRotation fur case 3. On the next page are all the methods from the AVLTree class and its nested Node class Which you might need to write the code for this question. Write the code for the method doubleRotation for the AVLTree class: private Node doubleRotation (Node k1, Node k2, Node k3 {
Explanation / Answer
DOUBLE ROTATION // RotateTwice -- static member function to rotate a given node // twice for the given direction in order to // restore the balance of an AVL tree. // Return 1 if the tree height changes due to rotation, // otherwise return 0. // template int AvlNode::RotateTwice(AvlNode * & root, dir_t dir) { dir_t otherDir = Opposite(dir); AvlNode * oldRoot = root; AvlNode * oldOtherDirSubtree = root->mySubtree[otherDir]; // assign new root root = oldRoot->mySubtree[otherDir]->mySubtree[dir]; // new-root exchanges it's "dir" subtree for it's grandparent oldRoot->mySubtree[otherDir] = root->mySubtree[dir]; root->mySubtree[dir] = oldRoot; // new-root exchanges it's "other-dir" subtree for it's parent oldOtherDirSubtree->mySubtree[dir] = root->mySubtree[otherDir]; root->mySubtree[otherDir] = oldOtherDirSubtree; // update balances root->mySubtree[LEFT]->myBal = -MAX(root->myBal, 0); root->mySubtree[RIGHT]->myBal = -MIN(root->myBal, 0); root->myBal = 0; // A double rotation always shortens the overall height of the tree return HEIGHT_CHANGE; } // Use mnemonic constants for valid balance-factor values enum balance_t { LEFT_HEAVY = -1, BALANCED = 0, RIGHT_HEAVY = 1 }; // Return true if the tree is too heavy on the left side inline static int LEFT_IMBALANCE(short bal) { return (bal RIGHT_HEAVY); } // Rebalance -- static member function to rebalance a (sub)tree // if it has become imbalanced. // Return 1 if the tree height changes due to rotation, // otherwise return 0. template int AvlNode::ReBalance(AvlNode * & root) { int heightChange = HEIGHT_NOCHANGE; if (LEFT_IMBALANCE(root->myBal)) { // Need a right rotation if (root->mySubtree[LEFT]->myBal == RIGHT_HEAVY) { // RL rotation needed heightChange = RotateTwice(root, RIGHT); } else { // RR rotation needed heightChange = RotateOnce(root, RIGHT); } } else if (RIGHT_IMBALANCE(root->myBal)) { // Need a left rotation if (root->mySubtree[RIGHT]->myBal == LEFT_HEAVY) { // LR rotation needed heightChange = RotateTwice(root, LEFT); } else { // LL rotation needed heightChange = RotateOnce(root, LEFT); } } return heightChange; } template avlNode* avlNode::balance () { // balance tree rooted at node // using single or double rotations as appropriate if (balanceFactor < 0) { if (left->balanceFactor singleRotateLeft(); return singleRotateRight(); } } else { if (right->balanceFactor >= 0) return singleRotateLeft(); else { // perform double rotation right = right->singleRotateRight(); return singleRotateLeft(); } } } Insertion method for our AVL tree template Comparable * AvlNode::Insert(Comparable * item, AvlNode * & root, int & change) { // See if the tree is empty if (root == NULL) { // Insert new node here root = new AvlNode(item); change = HEIGHT_CHANGE; return NULL; } // Initialize Comparable * found = NULL; int increase = 0; // Compare items and determine which direction to search cmp_t result = root->Compare(item->Key()); dir_t dir = (result == MIN_CMP) ? LEFT : RIGHT; if (result != EQ_CMP) { // Insert into "dir" subtree found = Insert(item, root->mySubtree[dir], change); if (found) return found; // already here - dont insert increase = result * change; // set balance factor increment } else { // key already in tree at this node increase = HEIGHT_NOCHANGE; return root->myData; } root->myBal += increase; // update balance factor // re-balance if needed -- height of current tree increases only if its // subtree height increases and the current tree needs no rotation. change = (increase && root->myBal) ? (1 - ReBalance(root)) : HEIGHT_NOCHANGE; return NULL; } Deletion method for our AVL tree template Comparable * AvlNode::Delete(KeyType key, AvlNode * & root, int & change, cmp_t cmp) { // See if the tree is empty if (root == NULL) { // Key not found change = HEIGHT_NOCHANGE; return NULL; } // Initialize Comparable * found = NULL; int decrease = 0; // Compare items and determine which direction to search cmp_t result = root->Compare(key, cmp); dir_t dir = (result == MIN_CMP) ? LEFT : RIGHT; if (result != EQ_CMP) { // Delete from "dir" subtree found = Delete(key, root->mySubtree[dir], change, cmp); if (! found) return found; // not found - can't delete decrease = result * change; // set balance factor decrement } else { // Found key at this node found = root->myData; // set return value // At this point we know "result" is zero and "root" points to // the node that we need to delete. There are three cases: // 1) The node is a leaf. Remove it and return. // 2) The node is a branch (has only 1 child). Make "root" // (the pointer to this node) point to the child. // 3) The node has two children. if ((root->mySubtree[LEFT] == NULL) && (root->mySubtree[RIGHT] == NULL)) { // We have a leaf -- remove it delete root; root = NULL; change = HEIGHT_CHANGE; // height changed from 1 to 0 return found; } else if ((root->mySubtree[LEFT] == NULL) || (root->mySubtree[RIGHT] == NULL)) { // We have one child -- only child becomes new root AvlNode * toDelete = root; root = root->mySubtree[(root->mySubtree[RIGHT]) ? RIGHT : LEFT]; change = HEIGHT_CHANGE; // We just shortened the subtree // Null-out the subtree pointers so we dont recursively delete toDelete->mySubtree[LEFT] = toDelete->mySubtree[RIGHT] = NULL; delete toDelete; return found; } else { // We have two children: find successor and replace our current data item with that of the successor root->myData = Delete(key, root->mySubtree[RIGHT], decrease, MIN_CMP); } } root->myBal -= decrease; // update balance factor // Rebalance if necessary if (decrease) { if (root->myBal) { change = ReBalance(root); // rebalance and see if height changed } else { change = HEIGHT_CHANGE; // balanced because subtree decreased } } else { change = HEIGHT_NOCHANGE; } return found; }