树的插入删除旋转归纳
Posted Leo_wlCnBlogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树的插入删除旋转归纳相关的知识,希望对你有一定的参考价值。
AVL 树的插入、删除、旋转归纳
/**
* Created by CG on 16/11/20.
*/
var TreeNode = function(){
this.parent = null;
this.left = null;
this.right = null;
this.value = null;
};
var AVLTree = {
insert : function (value) {
this.log("新加节点:new add: " + value);
if(this._tree == null){
var node = new TreeNode();
node.value = value;
this._tree = node;
return;
}
var newNode = new TreeNode();
newNode.value = value;
var currNode = this._tree;
while(true){
if(currNode == null){
this.log(" ======== currNode: null");
return;
}
//走向左子树
if(value <= currNode.value){
this.log(" to left: value: " + value + " currValue: " + currNode.value);
if(currNode.left){
currNode = currNode.left;
continue;
}
else {
newNode.parent = currNode;
currNode.left = newNode;
this.balanceTree(currNode, newNode);
break;
}
}
//走向右子树
else {
this.log(" to right: value: " + value + " currValue: " + currNode.value);
if(currNode.right){
currNode = currNode.right;
continue;
}
else {
newNode.parent = currNode;
currNode.right = newNode;
this.balanceTree(currNode, newNode);
break;
}
}
}
},
balanceTree : function (currNode, newNode) {
if(!currNode){
return;
}
this.printTreeByLevel();
while(currNode){
this.log("---------===========--- check if adjust: " + currNode.value);
if(currNode.parent){
this.log(" parent: " + currNode.parent.value);
}
var leftDepth = this.calcuDepth(currNode.left);
var rightDepth = this.calcuDepth(currNode.right);
this.log("leftDepth: " + leftDepth + " rightDepth: " + rightDepth);
if(leftDepth - rightDepth == 2){
if(newNode == null){
this.rightRotate(currNode);
}
else if(newNode.value < currNode.value && newNode.value < currNode.left.value){
this.log("LL");
this.rightRotate(currNode);
}
else if(newNode.value < currNode.value && newNode.value > currNode.left.value){
this.log("LR");
this.leftRotate(currNode.left);
this.rightRotate(currNode);
}
}
else if(rightDepth - leftDepth == 2){
if(newNode == null){
this.leftRotate(currNode);
}
else if(newNode.value > currNode.value && newNode.value > currNode.right.value){
this.log("RR");
this.leftRotate(currNode);
}
else if(newNode.value > currNode.value && newNode.value < currNode.right.value){
this.log("RL");
this.rightRotate(currNode.right);
this.leftRotate(currNode);
}
}
currNode = currNode.parent;
this.printTreeByLevel();
}
},
leftRotate : function (currNode) {
this.log("leftRotate: " + currNode.value);
var oldRight = currNode.right;
//如果当前节点就是根节点,更新外界引用的根节点
if(currNode == this._tree){
this._tree = oldRight;
}
else {
//更新变动前的 currNode 的 parent 的指向
if(currNode.parent.left == currNode){
currNode.parent.left = oldRight;
}
else if(currNode.parent.right == currNode){
currNode.parent.right = oldRight;
}
}
//更新 curr 和 oldRight 的 parent
oldRight.parent = currNode.parent;
//更新 curr 和 oldRight 的 child
currNode.right = oldRight.left;
if(oldRight.left){
oldRight.left.parent = currNode;
}
oldRight.left = currNode;
currNode.parent = oldRight;
this._tree.parent = null;
return oldRight;
},
rightRotate : function (currNode) {
this.log("rightRotate: " + currNode.value);
var oldLeft = currNode.left;
//如果当前节点就是根节点,更新外界引用的根节点
if(currNode == this._tree){
this._tree = oldLeft;
}
else {
//更新变动前的 currNode 的 parent 的指向
if(currNode.parent.left == currNode){
currNode.parent.left = oldLeft;
}
else if(currNode.parent.right == currNode){
currNode.parent.right = oldLeft;
}
}
//更新 curr 和 oldLeft 的 parent
oldLeft.parent = currNode.parent;
//更新 curr 和 oldLeft 的 child
currNode.left = oldLeft.right;
if(oldLeft.right){
oldLeft.right.parent = currNode;
}
oldLeft.right = currNode;
currNode.parent = oldLeft;
this._tree.parent = null;
return oldLeft;
},
/**
* 计算左右节点的深度。叶子节点的深度都是 1,依次向上加 1
* @param treeNode
* @returns {number}
*/
calcuDepth : function (treeNode) {
if(!treeNode){
return 0;
}
if(treeNode.left == null && treeNode.right == null){
return 1;
}
return 1 + Math.max(this.calcuDepth(treeNode.left), this.calcuDepth(treeNode.right));
},
/**
* 从树中删除一个节点
* @param value
*/
remove : function (value) {
this.log(" ===== 将要删除元素:" + value);
if(!value){
return;
}
//定位到节点
var currNode = this._tree;
while(currNode){
if(currNode.value == value){
break;
}
currNode = value > currNode.value ? currNode.right : currNode.left;
}
if(currNode.value != value){
this.log("没找到啊");
return;
}
var targetNode = null;
//删除该节点
if(currNode.left){
//有左子树,找到其中最大值来替代空位
targetNode = this.findMaxNode(currNode.left);
this.log(" == currNode.left: " + targetNode.value);
//更新 target 父节点的 child 指向
if(targetNode.parent != currNode){
var newChild = targetNode.left ? targetNode.left : targetNode.right;
if(targetNode.parent.left == targetNode){
targetNode.parent.left = newChild;
}
else {
targetNode.parent.right = newChild;
}
}
//更新 target 的 parent 指向
targetNode.parent = currNode.parent;
// 更新 target 的 right 指向
targetNode.right = currNode.right;
if(currNode.right){
currNode.right.parent = targetNode;
}
// 更新 target 的 left 指向 、、一定要注意避免自身死循环
if(currNode.left != targetNode){
targetNode.left = currNode.left;
currNode.left.parent = targetNode;
}
}
//没有左子树,但是有右子树,直接把右子树提上去就好了
else if(currNode.right){
targetNode = currNode.right;
targetNode.parent = currNode.parent;
this.log(" == currNode.right: " + targetNode.value);
}
//如果 curr 是叶子节点,只要更新 curr 的 parent 就可以了,没有额外处理
//更新 curr 父节点的 child 指向
if(currNode.parent && currNode.parent.left == currNode){
currNode.parent.left = targetNode;
}
else if(currNode.parent && currNode.parent.right == currNode){
currNode.parent.right = targetNode;
}
else {
this._tree = targetNode; //说明是 根节点
}
this.log(" +++++++++++++ ");
this.printTreeByLevel();
this.balanceTree(targetNode == null ? currNode.parent : targetNode);
this.log(" +++++++++++++ ");
},
findMaxNode : function(treeNode){
while(treeNode){
if(treeNode.right){
treeNode = treeNode.right;
}
else {
return treeNode;
}
}
return treeNode;
},
log : function (str) {
console.log(str);
},
/**
* 按照层级打印一棵树的各层节点名字
**/
printTreeByLevel : function () {
this.log("-----------------------");
if(!this._tree){
this.log(" === empty ===");
return;
}
var nodeList = [];
nodeList.push(this._tree);
while(nodeList.length > 0){
var len = nodeList.length;
var value = "";
for(var i=0; i<len; ++i){
var currNode = nodeList[i];
value += currNode.value + " ";
if(currNode.left){
nodeList.push(currNode.left);
}
if(currNode.right){
nodeList.push(currNode.right);
}
}
this.log(value);
nodeList = nodeList.slice(len);
}
},
};
AVLTree.printTreeByLevel();
AVLTree.log("====================================================================================================");
var list = [3,7,9,23,45, 1,5,14,25,24, 13,11, 26];
for(var index in list){
AVLTree.insert(list[index]);
}
AVLTree.log("====================================================================================================");
AVLTree.printTreeByLevel();
// AVLTree.remove(1);
// AVLTree.remove(25);
// AVLTree.printTreeByLevel();
以上是关于树的插入删除旋转归纳的主要内容,如果未能解决你的问题,请参考以下文章