1 var define = require("../define").define, 2 Tree = require("./Tree"), 3 base = require("../base"), 4 multiply = base.string.multiply; 5 6 var abs = Math.abs; 7 8 9 10 var makeNode = function(data) { 11 return { 12 data : data, 13 balance : 0, 14 left : null, 15 right : null 16 } 17 }; 18 19 var rotateSingle = function(root, dir, otherDir) { 20 var save = root[otherDir]; 21 root[otherDir] = save[dir]; 22 save[dir] = root; 23 return save; 24 }; 25 26 27 var rotateDouble = function(root, dir, otherDir) { 28 root[otherDir] = rotateSingle(root[otherDir], otherDir, dir); 29 return rotateSingle(root, dir, otherDir); 30 }; 31 32 var adjustBalance = function(root, dir, bal) { 33 var otherDir = dir == "left" ? "right" : "left"; 34 var n = root[dir], nn = n[otherDir]; 35 if (nn.balance == 0) 36 root.balance = n.balance = 0; 37 else if (nn.balance == bal) { 38 root.balance = -bal; 39 n.balance = 0; 40 } 41 else { /* nn.balance == -bal */ 42 root.balance = 0; 43 n.balance = bal; 44 } 45 nn.balance = 0; 46 }; 47 48 var insertAdjustBalance = function(root, dir) { 49 var otherDir = dir == "left" ? "right" : "left"; 50 51 var n = root[dir]; 52 var bal = dir == "left" ? -1 : +1; 53 54 if (n.balance == bal) { 55 root.balance = n.balance = 0; 56 root = rotateSingle(root, otherDir, dir); 57 } 58 else { 59 adjustBalance(root, dir, bal); 60 root = rotateDouble(root, otherDir, dir); 61 } 62 63 return root; 64 65 }; 66 67 var removeAdjustBalance = function(root, dir, done) { 68 var otherDir = dir == "left" ? "right" : "left"; 69 var n = root[otherDir]; 70 var bal = dir == "left" ? -1 : 1; 71 if (n.balance == -bal) { 72 root.balance = n.balance = 0; 73 root = rotateSingle(root, dir, otherDir); 74 } 75 else if (n.balance == bal) { 76 adjustBalance(root, otherDir, -bal); 77 root = rotateDouble(root, dir, otherDir); 78 } 79 else { /* n.balance == 0 */ 80 root.balance = -bal; 81 n.balance = bal; 82 root = rotateSingle(root, dir, otherDir); 83 done.done = true; 84 } 85 return root; 86 }; 87 88 var insert = function(root, data, done, compare) { 89 if (root == null || root == undefined) 90 root = makeNode(data); 91 else { 92 var dir = compare(data, root.data) == -1 ? "left" : "right"; 93 root[dir] = insert(root[dir], data, done, compare); 94 95 if (!done.done) { 96 /* Update balance factors */ 97 root.balance += dir == "left" ? -1 : 1; 98 /* Rebalance as necessary and terminate */ 99 if (root.balance == 0) 100 done.done = true; 101 else if (abs(root.balance) > 1) { 102 root = insertAdjustBalance(root, dir); 103 done.done = true; 104 } 105 } 106 } 107 108 return root; 109 }; 110 111 var remove = function(root, data, done, compare) { 112 var dir, cmp, save, b; 113 if (root) { 114 //Remove node 115 cmp = compare(data, root.data); 116 if (cmp === 0) { 117 // Unlink and fix parent 118 var l = root.left, r = root.right; 119 if (!l || !r) { 120 dir = !l ? "right" : "left"; 121 save = root[dir]; 122 return save; 123 } 124 else { 125 var heir = l, r; 126 while ((r = heir.right) != null) { 127 heir = r; 128 } 129 root.data = heir.data; 130 //reset and start searching 131 data = heir.data; 132 } 133 } 134 dir = compare(root.data, data) == -1 ? "right" : "left"; 135 root[dir] = remove(root[dir], data, done, compare); 136 if (!done.done) { 137 /* Update balance factors */ 138 b = (root.balance += (dir == "left" ? 1 : -1)); 139 /* Terminate or rebalance as necessary */ 140 var a = abs(b); 141 if (a === 1) 142 done.done = true; 143 else if (a > 1) 144 root = removeAdjustBalance(root, dir, done); 145 } 146 } 147 return root; 148 }; 149 150 151 /** 152 * @class <p>An AVL tree is a self-balancing binary search tree. 153 * In an AVL tree, the heights of the two child subtrees of any node differ by at most one. 154 * Lookup, insertion, and deletion all take O(log n) time in both the average and worst cases, 155 * where n is the number of nodes in the tree prior to the operation. 156 * Insertions and deletions may require the tree to be rebalanced by one or more tree rotations.</p> 157 * <p>AVL trees are more rigidly balanced than red-black trees, leading to slower insertion and removal but faster retrieval</p> 158 * 159 * <b>Performance</b> 160 * <table> 161 * <tr><td></td><td>Best</td><td>Worst</td></tr> 162 * <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr> 163 * <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr> 164 * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr> 165 * <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr> 166 * <table> 167 * @name AVLTree 168 * @augments comb.collections.Tree 169 * @memberOf comb.collections 170 */ 171 module.exports = exports = define(Tree, { 172 instance : { 173 /**@lends comb.collections.AVLTree.prototype*/ 174 175 insert : function(data) { 176 var done = {done : false}; 177 this.__root = insert(this.__root, data, done, this.compare); 178 }, 179 180 181 remove : function(data) { 182 this.__root = remove(this.__root, data, {done : false}, this.compare); 183 }, 184 185 __printNode : function(node, level) { 186 var str = []; 187 if (node == null) { 188 str.push(multiply('\t', level)); 189 str.push("~"); 190 console.log(str.join("")); 191 } else { 192 this.__printNode(node.right, level + 1); 193 str.push(multiply('\t', level)); 194 str.push(node.data + ":" + node.balance + "\n"); 195 console.log(str.join("")); 196 this.__printNode(node.left, level + 1); 197 } 198 } 199 200 } 201 }); 202 203