|
93 | 93 | return true; |
94 | 94 | }; |
95 | 95 |
|
| 96 | + /** |
| 97 | + * Gets the nodes to be restructured during an AVL restructure |
| 98 | + * after a remove/delete takes place. |
| 99 | + * |
| 100 | + * @public |
| 101 | + * @method |
| 102 | + * @param {Array} traveledNodes Array of previously traveled nodes |
| 103 | + * that are used to help determine the nodes to be restructured. |
| 104 | + */ |
| 105 | + exports.AVLTree.prototype._getNodesToRestructureRemove = |
| 106 | + function (traveledNodes) { |
| 107 | + // z is last traveled node - imbalance found at z |
| 108 | + var zIndex = traveledNodes.length; |
| 109 | + zIndex -= 1; |
| 110 | + var z = traveledNodes[zIndex]; |
| 111 | + // y should be child of z with larger height |
| 112 | + // (cannot be ancestor of removed node) |
| 113 | + var y; |
| 114 | + if (z._left !== null && z._right !== null){ |
| 115 | + y = (z._left === y) ? z._right : z._left; |
| 116 | + }else if (z._left !== null && z._right === null){ |
| 117 | + y = z._left; |
| 118 | + }else if (z._right !== null && z._left === null){ |
| 119 | + y = z._right; |
| 120 | + } |
| 121 | + // x should be tallest child of y. |
| 122 | + // If children same height, x should be child of y |
| 123 | + // that has same orientation as z to y. |
| 124 | + var x; |
| 125 | + if (y._left !== null && y._right !== null){ |
| 126 | + if (y._left._height > y._right._height){ |
| 127 | + x = y._left; |
| 128 | + }else if (y._left._height < y._right._height){ |
| 129 | + x = y._right; |
| 130 | + }else if (y._left._height === y._right._height){ |
| 131 | + x = (z._left === y) ? y._left : y._right; |
| 132 | + } |
| 133 | + }else if (y._left !== null && y._right === null){ |
| 134 | + x = y._left; |
| 135 | + }else if (y._right !== null && y._left === null){ |
| 136 | + x = y._right; |
| 137 | + } |
| 138 | + return [x, y, z]; |
| 139 | + }; |
| 140 | + |
| 141 | + /** |
| 142 | + * Gets the nodes to be restructured during an AVL restructure |
| 143 | + * after an insert takes place. |
| 144 | + * |
| 145 | + * @public |
| 146 | + * @method |
| 147 | + * @param {Array} traveledNodes Array of previously traveled nodes |
| 148 | + * that are used to help determine the nodes to be restructured. |
| 149 | + */ |
| 150 | + exports.AVLTree.prototype._getNodesToRestructureInsert = |
| 151 | + function (traveledNodes) { |
| 152 | + // z is last traveled node - imbalance found at z |
| 153 | + var zIndex = traveledNodes.length; |
| 154 | + zIndex -= 1; |
| 155 | + var z = traveledNodes[zIndex]; |
| 156 | + // y should be child of z with larger height |
| 157 | + // (must be ancestor of inserted node) |
| 158 | + // therefore, last traveled node is correct. |
| 159 | + var yIndex = traveledNodes.length; |
| 160 | + yIndex -= 2; |
| 161 | + var y = traveledNodes[yIndex]; |
| 162 | + // x should be tallest child of y. |
| 163 | + // If children same height, x should be ancestor |
| 164 | + // of inserted node (in traveled path). |
| 165 | + var x; |
| 166 | + if (y._left !== null && y._right !== null){ |
| 167 | + if (y._left._height > y._right._height){ |
| 168 | + x = y._left; |
| 169 | + }else if (y._left._height < y._right._height){ |
| 170 | + x = y._right; |
| 171 | + }else if (y._left._height === y._right._height){ |
| 172 | + var xIndex = traveledNodes.length; |
| 173 | + xIndex -= 3; |
| 174 | + x = traveledNodes[xIndex]; |
| 175 | + } |
| 176 | + }else if (y._left !== null && y._right === null){ |
| 177 | + x = y._left; |
| 178 | + }else if (y._right !== null && y._left === null){ |
| 179 | + x = y._right; |
| 180 | + } |
| 181 | + return [x, y, z]; |
| 182 | + }; |
| 183 | + |
96 | 184 | /** |
97 | 185 | * Maintains the height balance property by |
98 | 186 | * walking to root and checking for invalid height |
|
102 | 190 | * @public |
103 | 191 | * @method |
104 | 192 | * @param {Node} node Started node. |
| 193 | + * @param {Boolean} isRemove Represents if method was called after remove. |
105 | 194 | */ |
106 | | - exports.AVLTree.prototype._maintainHeightBalanceProperty = function (node) { |
| 195 | + exports.AVLTree.prototype._maintainHeightBalanceProperty = |
| 196 | + function (node, isRemove) { |
107 | 197 | var current = node; |
108 | | - var path = []; //During restructure, use last 3 nodes traveled. |
| 198 | + var traveledNodes = []; |
109 | 199 | while (current !== null){ |
110 | | - path.push(current); |
| 200 | + traveledNodes.push(current); |
111 | 201 | current._height = this._getHeightAtNode(current); |
112 | 202 | if (!this._isBalancedAtNode(current)){ |
113 | | - if (path.length >= 3){ |
114 | | - var nodesToRestructure = path.slice(0, 3); |
115 | | - var x = nodesToRestructure[0]; |
116 | | - var y = nodesToRestructure[1]; |
117 | | - var z = nodesToRestructure[2]; |
118 | | - this._restructure(x, y, z); |
119 | | - } |
| 203 | + var nodesToBeRestructured = (isRemove) |
| 204 | + ? this._getNodesToRestructureRemove(traveledNodes) |
| 205 | + : this._getNodesToRestructureInsert(traveledNodes); |
| 206 | + this._restructure(nodesToBeRestructured); |
120 | 207 | } |
121 | 208 | current = current._parent; |
122 | 209 | } |
|
128 | 215 | * |
129 | 216 | * @public |
130 | 217 | * @method |
131 | | - * @param {Node} x node with lowest height to be restructured. |
132 | | - * @param {Node} y parent of x parameter. |
133 | | - * @param {Node} z grandparent of x, largest height. |
| 218 | + * @param {Array} nodesToBeRestructured |
| 219 | + * array of nodes, in format, [x, y, z], to be restructured |
134 | 220 | */ |
135 | | - exports.AVLTree.prototype._restructure = function (x, y, z) { |
| 221 | + exports.AVLTree.prototype._restructure = function (nodesToBeRestructured) { |
| 222 | + var x = nodesToBeRestructured[0]; |
| 223 | + var y = nodesToBeRestructured[1]; |
| 224 | + var z = nodesToBeRestructured[2]; |
136 | 225 | //Determine Rotation Pattern |
137 | 226 | if (z._right === y && y._right === x){ |
138 | 227 | this._rightRight(x, y, z); |
|
337 | 426 | } |
338 | 427 | if (!current[insertKey]) { |
339 | 428 | current[insertKey] = new exports.Node(value, null, null, current); |
340 | | - this._maintainHeightBalanceProperty(current[insertKey]); |
| 429 | + this._maintainHeightBalanceProperty(current[insertKey], false); |
341 | 430 | } else { |
342 | 431 | this.insert(value, current[insertKey]); |
343 | 432 | } |
|
481 | 570 | */ |
482 | 571 | exports.AVLTree.prototype._replaceChild = |
483 | 572 | function (parent, oldChild, newChild) { |
484 | | - if (!parent) { |
| 573 | + if (parent === null) { |
485 | 574 | this._root = newChild; |
486 | | - this._root._parent = null; |
| 575 | + if (this._root !== null){ |
| 576 | + this._root._parent = null; |
| 577 | + } |
487 | 578 | } else { |
488 | 579 | if (parent._left === oldChild) { |
489 | 580 | parent._left = newChild; |
|
501 | 592 | * Average runtime complexity: O(log N). |
502 | 593 | * |
503 | 594 | * @public |
504 | | - * @param {Node} node to be removed |
| 595 | + * @param {Number|String} value of node to be removed |
505 | 596 | * @returns {Boolean} True/false depending |
506 | 597 | * on whether the given node is removed. |
507 | 598 | */ |
508 | | - exports.AVLTree.prototype.remove = function (node) { |
| 599 | + exports.AVLTree.prototype.remove = function (value) { |
| 600 | + var node = this.find(value); |
509 | 601 | if (!node) { |
510 | 602 | return false; |
511 | 603 | } |
512 | | - |
513 | 604 | if (node._left && node._right) { |
514 | 605 | var min = this._findMin(node._right); |
515 | 606 | var temp = node.value; |
516 | | - |
517 | 607 | node.value = min.value; |
518 | 608 | min.value = temp; |
519 | 609 | return this.remove(min); |
520 | 610 | } else { |
521 | | - if (node._parent !== null) { |
522 | | - if (node._left) { |
523 | | - this._replaceChild(node._parent, node, node._left); |
524 | | - } else if (node._right) { |
525 | | - this._replaceChild(node._parent, node, node._right); |
526 | | - } else { |
527 | | - this._replaceChild(node._parent, node, null); |
528 | | - } |
529 | | - }else { |
530 | | - this._root = null; |
| 611 | + if (node._left) { |
| 612 | + this._replaceChild(node._parent, node, node._left); |
| 613 | + this._maintainHeightBalanceProperty(node._left, true); |
| 614 | + } else if (node._right) { |
| 615 | + this._replaceChild(node._parent, node, node._right); |
| 616 | + this._maintainHeightBalanceProperty(node._right, true); |
| 617 | + } else { |
| 618 | + this._replaceChild(node._parent, node, null); |
| 619 | + this._maintainHeightBalanceProperty(node._parent, true); |
531 | 620 | } |
532 | 621 | return true; |
533 | 622 | } |
|
656 | 745 | * @returns {Node} The lowest common ancestor of the two nodes or null. |
657 | 746 | */ |
658 | 747 | exports.AVLTree.prototype.lowestCommonAncestor = |
659 | | - function (firstNode, secondNode) { |
| 748 | + function (firstNode, secondNode) { |
660 | 749 | return this._lowestCommonAncestor(firstNode, secondNode, this._root); |
661 | 750 | }; |
662 | 751 |
|
663 | 752 | exports.AVLTree.prototype._lowestCommonAncestor = |
664 | | - function (firstNode, secondNode, current) { |
| 753 | + function (firstNode, secondNode, current) { |
665 | 754 | var firstNodeInLeft = this._existsInSubtree(firstNode, current._left); |
666 | 755 | var secondNodeInLeft = this._existsInSubtree(secondNode, current._left); |
667 | 756 | var firstNodeInRight = this._existsInSubtree(firstNode, current._right); |
|
0 commit comments