1.06k likes | 1.27k Views
241-423 Advanced Data Structures and Algorithms. Objective s discuss various kinds of balanced search trees: AVL trees, 2-3-4 trees, Red-Black trees,. Semester 2, 2012-2013. 12. Balanced Search Trees. Contents. 1. What is a Balanced Binary Search Tree? 2. AVL Trees 3. 2-3-4 Trees
E N D
241-423 Advanced Data Structures and Algorithms Objectives discuss various kinds of balanced search trees: AVL trees, 2-3-4 trees, Red-Black trees, Semester 2, 2012-2013 12. Balanced Search Trees
Contents 1. What is a Balanced Binary Search Tree? 2. AVL Trees 3. 2-3-4 Trees 4. Red-Black Trees
1.What is a Balanced Binary Search Tree? • A balanced search tree is one where all the branches from the root have almost the same height. balanced unbalanced continued
As a tree becomes more unbalanced, search running time decreases from O(log n) to O(n) • because the tree shape turns into a list • We want to keep the binary search tree balanced as nodes are added/removed, so searching/insertion remain fast.
1.1. Balanced BSTs: AVL Trees • An AVL tree maintains height balance • for each node, the difference in height of its two subtrees is in the range -1 to 1
1.2. 2-3-4 Trees • A multiway tree where each node has at most 4 children, and a node can hold up to 3 values. • A 2-3-4- tree can be perfectly balanced • no difference in height between branches • requires complex nodes and links
1.3. Red-Black Trees • A red-black tree is a binary version of a 2-3-4 tree • the nodes have a 'color' attribute: BLACK or RED • drawn in Ford and Topp (and here) in white and gray!! • the tree maintains a balance measure called the BLACK height BLACK RED
1.4. B-Trees • A multiway tree where each node has at most m children, and a node can hold up to m-1 values • a more general version of a 2-3-4 tree • B-Trees are most commonly used in databases and filesystems • most nodes are stored in secondary storage such as hard drives
2. AVL Trees • For each AVL tree node, the difference between the heights of its left and right subtrees is either -1, 0 or +1 • this is called the balance factor of a node • balanceFactor = height(left subtree) - height(right subtree) • if balanceFactor > 1 or < -1 then the tree is too unbalanced, and needs 'rearranging' to make it more balanced L - R
Heaviness • if the balanceFactor is positive, then the node is "heavy on the left" • the height of the left subtree is greater than the height of the right subtree • a negative balanceFactor, means the node is "heavy on the right" continued
L – R = 1-2 L – R = 2-1 L – R= 0-1 root is heavy on the right, but still balanced root is heavy on the left, but still balanced root is heavy on the right, but still balanced
Using AVLTree String[] stateList = {"NV", "NY", "MA", "CA", "GA"}; AVLTree<String> avltreeA = new AVLTree<String>(); for (int i = 0; i < stateList.length; i++) avltreeB.add(stateList[i]); System.out.println("States: " + avltreeA); int[] arr = {50, 95, 60, 90, 70, 80, 75, 78}; AVLTree<Integer> avltreeB = new AVLTree<Integer>(); for (int i = 0; i < arr.length; i++) avltreeB.add(arr[i]); // display the tree System.out.println(avltreeB.displayTree(2)); avltreeB.drawTree(2);
Execution States: [CA, GA, MA, NV, NY] 70(-1) 60(1) 90(1) 50(0) 78(0) 95(0) 75(0) 80(0) root is heavy on the right, but still balanced (-1) (1)
The AVLTree Node • An AVLNode contains the node's value, references to the node's two subtrees, and the node height.height(node) = max ( height(node.left), height(node.right) ) + 1; nodeValue AVTTreeNode object height right left continued
nodeValue height private static class AVLNode<T> { public T nodeValue; // node data public int height; // child links public AVLNode<T> left, right; public AVLNode (T item) { nodeValue = item; height = 0; left = null; right = null; } } right left Use of public is bad; the coding style is due to Ford & Topp
2.2. Adding a Node to the Tree • The addition of a node may cause the tree to go out of balance. • addNode() adds a node and may reorder nodes as it returns from the adding back to the root • reordering is the new idea in AVL trees • the reordering is done using single and double rotations
2.2.1. Too Heavy on the Left Two cases L – R = 3-1 or 25 25 node P node P (2) left left (2) 12 30 12 30 left right 5 20 5 20 left or right branch -- doesn't matter branch doesn't matter 11 22 inside grandchild outside grandchild continued
Inserting a node in the left subtree of P (e.g. adding 11 or 22) may cause P to become "too heavy on the left" • balance factor == 2 • The new node can either be in the outside or inside grandchild subtree: • outside grandchild = left-left • inside grandchild = left-right continued
2.2.2. Too Heavy on the Right Two cases L – R = 1-3 or 25 25 node P node P (-2) (-2) right right 12 30 12 30 right left 27 45 27 45 branch doesn't matter branch doesn't matter 40 29 inside grandchild outside grandchild continued
Inserting a node in the right subtree of P (e.g. adding 29 or 40) may cause P to become "too heavy on the right" • balance factor == -2 • The new node can either be in the outside or inside grandchild subtree: • outside grandchild = right-right • inside grandchild = right-left continued
2.3. Single Rotations • When a new item is added to the subtree for an outside grandchild, the imbalance is fixed with a single right or left rotation • Two cases: • left outside grandchild (left-left) --> single right rotation • right outside grandchild (right-right) --> single left rotation
2.3.1. Single Right Rotation • A single right rotation occurs when a new element is added to the subtree of the left outside grandchild (left-left) continued
1 add 3 cut cut add 2 4 left outside grandchild (left-left) continued
A single right rotationrotates the left child (LC) to replace the parent • the parent becomes the new right child • The right subtree of LC (RGC) is attached as a left child of P • ok since the nodes in RGC are greater than LC but less than P
singleRotateRight() // single right rotation on p private static <T> AVLNode<T> singleRotateRight( AVLNode<T> p) { AVLNode<T> lc = p.left; p.left = lc.right; // 1 & 4 on slide 26 lc.right = p; // 2 & 3 p.height = max(height(p.left), height(p.right)) + 1; lc.height = max(height(lc.left), height(rc.right)) + 1; return lc; }
private static <T> int height(AVLNode<T> t) { if (t == null) return -1; else return t.height; }
2.3.2. Single Left Rotation • A single left rotation occurs when a new element is added to the subtree of the right outside grandchild (right-right). • The rotation exchanges the parent (P) and right child (RC) nodes, and attaches the subtree LGC as the right subtree of P. continued
1 cut 3 add 2 add cut 4 right outside grandchild (right-right)
singleRotateLeft() // single left rotation on p private static <T> AVLNode<T> singleRotateLeft( AVLNode<T> p) { AVLNode<T> rc = p.right; p.right = rc.left; // 1 & 4 on slide 31 rc.left = p; // 2 & 3 p.height = max(height(p.left),height(p.right)) + 1; rc.height = max(height(rc.left), height(rc.right)) + 1; return rc; }
2.4. Double Rotations • When a new item is added to the subtree for an inside grandchild, the imbalance is fixed with a double right or left rotation • a double rotation is two single rotations • Two cases: • left inside grandchild (left-right) --> double right rotation • right inside grandchild (right-left) --> double left rotation
2.4.1. A Double Right Rotation R L Single left rotation about LC balanced Single right rotation about P left inside grandchild(left-right) Watch RGC rise to the top
doubleRotateRight() private static <T> AVLNode<T> doubleRotateRight( AVLNode<T> p)/* double right rotation on p is left rotation, then right rotation */ { p.left = singleRotateLeft(p.left); return singleRotateRight(p); }
2.4.2. A Double Left Rotation L P P LGC RC R LGC LGC P RC LC RC LC A RGC A B A B LC B RGC RGC Single right rotation about RC balanced Single left rotation about P right inside grandchild(right-left) Watch LGC rise to the top
doubleRotateLeft() private static <T> AVLNode<T> doubleRotateLeft( AVLNode<T> p) /* double left rotation on p is right rotation, then left rotation */ { p.right = singleRotateRight(p.right); return singleRotateLeft(p); }
2.5. addNode() • addNode() recurses down to the insertion point and inserts the node. • As it returns, it visits the nodes in reverse order, fixing any imbalances using rotations. • It must handle four cases: • balance height == 2: left-left, left-right • balance height == -2: right-left, right-right
Basic addNode() No AVL rotation code added yet was P in earlier slides private Node<T> addNode(Node<T> t, T item) { if (t == null) // found insertion point t = new Node<T>(item); else if (((Comparable<T>)item).compareTo(t.nodeValue) < 0) { t.left = addNode( t.left, item); // visit left subtree else if (((Comparable<T>)item).compareTo(t.nodeValue) > 0 ) { t.right = addNode(t.right, item); // visit right else throw new IllegalStateException(); // duplicate error return t; } // end of addNode()
AVL rotation code added private AVLNode<T> addNode(AVLNode<T> t, T item) { if(t == null) // found insertion point t = new AVLNode<T>(item); else if (((Comparable<T>)item).compareTo(t.nodeValue) < 0) { // visit left subtree: add node then maybe rotate t.left = addNode( t.left, item); // add node, then... if (height(t.left) - height(t.right) == 2 ) { //too heavy on left if (((Comparable<T>)item).compareTo(t.left.nodeValue) < 0) // problem on left-left t = singleRotateRight(t); else // problem on left-right t = doubleRotateRight(t); // left then right rotation } } : continued
else if (((Comparable<T>)item).compareTo(t.nodeValue) > 0 ) { // visit right subtree: add node then maybe rotate t.right = addNode(t.right, item ); // add node, then... if (height(t.left)-height(t.right) == -2){ //too heavy on right if (((Comparable<T>)item).compareTo(t.right.nodeValue) > 0)// problem on right-right t = singleRotateLeft(t); else // problem on right-left t = doubleRotateLeft(t); // right then left rotation } } else // duplicate; throw IllegalStateException throw new IllegalStateException(); // calculate new height of t t.height = max(height(t.left), height(t.right)) + 1; return t; } // end of addNode()
add() public interface for inserting an item public boolean add(T item) { try { root = addNode(root, item); // start from root } catch (IllegalStateException e) { return false; } // item is a duplicate // increment the tree size and modCount treeSize++; modCount++; return true; // node was added ok }
2.6. Building an AVL Tree gray node is too heavy left outside grandchild(left-left) continued
right outside grandchild(right-right) 45 continued
right inside grandchild(right-left) double rotate left(right then left rotation) continued
left inside grandchild(left-right) double rotate right(left then right rotation) continued
2.7. Efficiency of AVL Tree Insertion • Detailed analysis shows:int(log2n) height < 1.4405 log2(n+2) - 1.3277 • So the worst case running time for insertion is O(log2n). • The worst case for deletion is also O(log2n).
2.8. Deletion in an AVL Tree Deletion can easily cause an imbalance e.g delete 32 44 44 17 62 17 62 32 50 78 50 78 88 88 48 54 48 54 after deletion before deletion of 32 AVL Trees 48
The numbers refer to the maximum number of branches that can leave the node 3. 2-3-4 Trees • In a 2-3-4 tree: • a 2‑node has 1 value and a max of 2 children • a 3-node has 2 values and a max of 3 children • a 4-node has 3 values and a max of 4 children same as a binary tree node
3.1. Searching a 2-3-4 Tree • To find an item: • start at the root and compare the item with all the values in the node; • if there's no match, move down to the appropriate subtree; • repeat until you find a match or reach an empty subtree