|
93 | 93 | return true; |
94 | 94 | }; |
95 | 95 |
|
| 96 | + exports.AVLTree.prototype._getNodesToRestructureRemove = |
| 97 | + function (traveledNodes) { |
| 98 | + // z is last traveled node - imbalance found at z |
| 99 | + var zIndex = traveledNodes.length; |
| 100 | + zIndex -= 1; |
| 101 | + var z = traveledNodes[zIndex]; |
| 102 | + // y should be child of z with larger height |
| 103 | + // (cannot be ancestor of removed node) |
| 104 | + var y; |
| 105 | + if (z._left !== null && z._right !== null){ |
| 106 | + y = (z._left === y) ? z._right : z._left; |
| 107 | + }else if (z._left !== null && z._right === null){ |
| 108 | + y = z._left; |
| 109 | + }else if (z._right !== null && z._left === null){ |
| 110 | + y = z._right; |
| 111 | + } |
| 112 | + // x should be tallest child of y. |
| 113 | + // If children same height, x should be child of y |
| 114 | + // that has same orientation as z to y. |
| 115 | + var x; |
| 116 | + if (y._left !== null && y._right !== null){ |
| 117 | + if (y._left._height > y._right._height){ |
| 118 | + x = y._left; |
| 119 | + }else if (y._left._height < y._right._height){ |
| 120 | + x = y._right; |
| 121 | + }else if (y._left._height === y._right._height){ |
| 122 | + x = (z._left === y) ? y._left : y._right; |
| 123 | + } |
| 124 | + }else if (y._left !== null && y._right === null){ |
| 125 | + x = y._left; |
| 126 | + }else if (y._right !== null && y._left === null){ |
| 127 | + x = y._right; |
| 128 | + } |
| 129 | + return [x, y, z]; |
| 130 | + }; |
| 131 | + |
| 132 | + exports.AVLTree.prototype._getNodesToRestructureInsert = |
| 133 | + function (traveledNodes) { |
| 134 | + // z is last traveled node - imbalance found at z |
| 135 | + var zIndex = traveledNodes.length; |
| 136 | + zIndex -= 1; |
| 137 | + var z = traveledNodes[zIndex]; |
| 138 | + // y should be child of z with larger height |
| 139 | + // (must be ancestor of inserted node) |
| 140 | + // therefore, last traveled node is correct. |
| 141 | + var yIndex = traveledNodes.length; |
| 142 | + yIndex -= 2; |
| 143 | + var y = traveledNodes[yIndex]; |
| 144 | + // x should be tallest child of y. |
| 145 | + // If children same height, x should be ancestor |
| 146 | + // of inserted node (in traveled path). |
| 147 | + var x; |
| 148 | + if (y._left !== null && y._right !== null){ |
| 149 | + if (y._left._height > y._right._height){ |
| 150 | + x = y._left; |
| 151 | + }else if (y._left._height < y._right._height){ |
| 152 | + x = y._right; |
| 153 | + }else if (y._left._height === y._right._height){ |
| 154 | + var xIndex = traveledNodes.length; |
| 155 | + xIndex -= 3; |
| 156 | + x = traveledNodes[xIndex]; |
| 157 | + } |
| 158 | + }else if (y._left !== null && y._right === null){ |
| 159 | + x = y._left; |
| 160 | + }else if (y._right !== null && y._left === null){ |
| 161 | + x = y._right; |
| 162 | + } |
| 163 | + return [x, y, z]; |
| 164 | + }; |
| 165 | + |
96 | 166 | /** |
97 | 167 | * Maintains the height balance property by |
98 | 168 | * walking to root and checking for invalid height |
|
102 | 172 | * @public |
103 | 173 | * @method |
104 | 174 | * @param {Node} node Started node. |
| 175 | + * @param {Boolean} isRemove Represents if method was called after remove. |
105 | 176 | */ |
106 | | - exports.AVLTree.prototype._maintainHeightBalanceProperty = function (node) { |
| 177 | + exports.AVLTree.prototype._maintainHeightBalanceProperty = |
| 178 | + function (node, isRemove) { |
107 | 179 | var current = node; |
108 | | - var path = []; //During restructure, use last 3 nodes traveled. |
| 180 | + var traveledNodes = []; |
109 | 181 | while (current !== null){ |
110 | | - path.push(current); |
| 182 | + traveledNodes.push(current); |
111 | 183 | current._height = this._getHeightAtNode(current); |
112 | 184 | 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 | | - } |
| 185 | + var nodesToBeRestructured = (isRemove) |
| 186 | + ? this._getNodesToRestructureRemove(traveledNodes) |
| 187 | + : this._getNodesToRestructureInsert(traveledNodes); |
| 188 | + this._restructure(nodesToBeRestructured); |
120 | 189 | } |
121 | 190 | current = current._parent; |
122 | 191 | } |
|
128 | 197 | * |
129 | 198 | * @public |
130 | 199 | * @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. |
| 200 | + * @param {Array} nodesToBeRestructured |
| 201 | + * array of nodes, in format, [x, y, z], to be restructured |
134 | 202 | */ |
135 | | - exports.AVLTree.prototype._restructure = function (x, y, z) { |
| 203 | + exports.AVLTree.prototype._restructure = function (nodesToBeRestructured) { |
| 204 | + var x = nodesToBeRestructured[0]; |
| 205 | + var y = nodesToBeRestructured[1]; |
| 206 | + var z = nodesToBeRestructured[2]; |
136 | 207 | //Determine Rotation Pattern |
137 | 208 | if (z._right === y && y._right === x){ |
138 | 209 | this._rightRight(x, y, z); |
|
337 | 408 | } |
338 | 409 | if (!current[insertKey]) { |
339 | 410 | current[insertKey] = new exports.Node(value, null, null, current); |
340 | | - this._maintainHeightBalanceProperty(current[insertKey]); |
| 411 | + this._maintainHeightBalanceProperty(current[insertKey], false); |
341 | 412 | } else { |
342 | 413 | this.insert(value, current[insertKey]); |
343 | 414 | } |
|
481 | 552 | */ |
482 | 553 | exports.AVLTree.prototype._replaceChild = |
483 | 554 | function (parent, oldChild, newChild) { |
484 | | - if (!parent) { |
| 555 | + if (parent === null) { |
485 | 556 | this._root = newChild; |
486 | | - this._root._parent = null; |
| 557 | + if (this._root !== null){ |
| 558 | + this._root._parent = null; |
| 559 | + } |
487 | 560 | } else { |
488 | 561 | if (parent._left === oldChild) { |
489 | 562 | parent._left = newChild; |
|
501 | 574 | * Average runtime complexity: O(log N). |
502 | 575 | * |
503 | 576 | * @public |
504 | | - * @param {Node} node to be removed |
| 577 | + * @param {Number|String} value of node to be removed |
505 | 578 | * @returns {Boolean} True/false depending |
506 | 579 | * on whether the given node is removed. |
507 | 580 | */ |
508 | | - exports.AVLTree.prototype.remove = function (node) { |
| 581 | + exports.AVLTree.prototype.remove = function (value) { |
| 582 | + var node = this.find(value); |
509 | 583 | if (!node) { |
510 | 584 | return false; |
511 | 585 | } |
512 | | - |
513 | 586 | if (node._left && node._right) { |
514 | 587 | var min = this._findMin(node._right); |
515 | 588 | var temp = node.value; |
516 | | - |
517 | 589 | node.value = min.value; |
518 | 590 | min.value = temp; |
519 | 591 | return this.remove(min); |
520 | 592 | } 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; |
| 593 | + if (node._left) { |
| 594 | + this._replaceChild(node._parent, node, node._left); |
| 595 | + this._maintainHeightBalanceProperty(node._left, true); |
| 596 | + } else if (node._right) { |
| 597 | + this._replaceChild(node._parent, node, node._right); |
| 598 | + this._maintainHeightBalanceProperty(node._right, true); |
| 599 | + } else { |
| 600 | + this._replaceChild(node._parent, node, null); |
| 601 | + this._maintainHeightBalanceProperty(node._parent, true); |
531 | 602 | } |
532 | 603 | return true; |
533 | 604 | } |
|
656 | 727 | * @returns {Node} The lowest common ancestor of the two nodes or null. |
657 | 728 | */ |
658 | 729 | exports.AVLTree.prototype.lowestCommonAncestor = |
659 | | - function (firstNode, secondNode) { |
| 730 | + function (firstNode, secondNode) { |
660 | 731 | return this._lowestCommonAncestor(firstNode, secondNode, this._root); |
661 | 732 | }; |
662 | 733 |
|
663 | 734 | exports.AVLTree.prototype._lowestCommonAncestor = |
664 | | - function (firstNode, secondNode, current) { |
| 735 | + function (firstNode, secondNode, current) { |
665 | 736 | var firstNodeInLeft = this._existsInSubtree(firstNode, current._left); |
666 | 737 | var secondNodeInLeft = this._existsInSubtree(secondNode, current._left); |
667 | 738 | var firstNodeInRight = this._existsInSubtree(firstNode, current._right); |
|
0 commit comments