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();
AVL 树的插入、删除、旋转归纳的更多相关文章
- AVL树的插入删除查找算法实现和分析-1
至于什么是AVL树和AVL树的一些概念问题在这里就不多说了,下面是我写的代码,里面的注释非常详细地说明了实现的思想和方法. 因为在操作时真正需要的是子树高度的差,所以这里采用-1,0,1来表示左子树和 ...
- AVL树的插入和删除
一.AVL 树 在计算机科学中,AVL树是最早被发明的自平衡二叉查找树.在AVL树中,任一节点对应的两棵子树的最大高度差为 1,因此它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下的时间复杂度 ...
- AVL树的插入操作(旋转)图解
=================================================================== AVL树的概念 在说AVL树的概念之前,我们需要清楚 ...
- AVL树的插入与删除
AVL 树要在插入和删除结点后保持平衡,旋转操作必不可少.关键是理解什么时候应该左旋.右旋和双旋.在Youtube上看到一位老师的视频对这个概念讲解得非常清楚,再结合算法书和网络的博文,记录如下. 1 ...
- AVL树的单双旋转操作
把必须重新平衡的节点称为å.对于二叉树,å的两棵子树的高度最多相差2,这种不平衡可能有四种情况: 对å的左儿子的左子树进行插入节点(左-左) 对å的左儿子的右子树进行插入节点(左-右) 对å的右儿子的 ...
- 创建AVL树,插入,删除,输出Kth Min
https://github.com/TouwaErioH/subjects/tree/master/C%2B%2B/PA2 没有考虑重复键,可以在结构体内加一个int times. 没有考虑删除不存 ...
- 第七章 二叉搜索树 (d2)AVL树:插入
- AVL树(平衡二叉查找树)
首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...
- 红黑树(RB-tree)比AVL树的优势在哪?
1. 如果插入一个node引起了树的不平衡,AVL和RB-Tree都是最多只需要2次旋转操作,即两者都是O(1):但是在删除node引起树的不平衡时,最坏情况下,AVL需要维护从被删node到root ...
随机推荐
- 两个常见Qt编译错误的解决
作者:朱金灿 来源:http://blog.csdn.net/clever101 如果在电脑上安装了两个Qt版本,在编译一个工程时有可能出现如下的编译错误: ERROR: failed to refr ...
- ocx 中使用CImage和CComPtr
#include <atlimage.h> using namespace ATL;
- OpenCV从入门到放弃(五):像素!
一.概念 1.图像本质上面是由数值组成的矩阵.矩阵中的一个元素相应一个像素. 2.对于灰度图像(黑白图像),像素是8位无符号数(CV_8U).0表示黑色,255表示白色.对于彩色图像,是用三原色数据合 ...
- pstack.sh 改进版
pstack.sh 改进版本 #!/bin/bash if (( $# < 1 )) ; then echo "usage: `basename $0` pid" 1> ...
- MySQL误删数据救命指南
预防误操作导致文件/数据丢失的建议: 1.欲删除文件时,将rm命令改成mv,可在系统层面将rm命令做个alias(或参考Windows / Mac OSX做法,删除文件时先进回收站).2.删除数据库. ...
- 【42.59%】【codeforces 602A】Two Bases
time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...
- 数学分析告诉偶们什么(vamei)
1]人生的痛苦在于追求错误的东西.所谓追求错误的东西,就是你在无限趋近于它的时候,才猛然发现,你和它是不连续的. 2]人和人就像数轴上的有理数点,彼此能够靠得非常近非常近,但你们之间始终存在隔阂. 3 ...
- [转载]Ocelot简易教程(二)之快速开始2
为什么这篇的标题叫“Ocelot简易教程(二)之快速开始2”呢,因为很多朋友跟我说上一篇“ Ocelot简易教程(二)之快速开始1”内容太少了,只是简单介绍Ocelot的一些简单配置,让Ocelot能 ...
- [Javascript] Combine Objects with Object.assign and Lodash merge
Learn how to use Object.assign to combine multiple objects together. This pattern is helpful when wr ...
- 小强的HTML5移动开发之路(46)——汇率计算器【2】
在上一篇中我们完成了汇率计算页面,下面来完成汇率设置页面的显示. <div class="setRates"> <div class="header&q ...