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 ...
随机推荐
- Oracle 11gR2 静默安装奇怪错误
在静默安装Oracle 11gR2 的时候发现的奇怪错误,有点摸不着头脑 【步骤一】配置静默文件只安装软件 #--------------------------------------------- ...
- 数学之路-python计算实战(7)-机器视觉-图像产生加性零均值高斯噪声
图像产生加性零均值高斯噪声.在灰度图上加上噪声,加上噪声的方式是每一个点的灰度值加上一个噪声值.噪声值的产生方式为Box-Muller算法生成高斯噪声. 在计算机模拟中,常常须要生成正态分布的数值.最 ...
- 应用层协议实现系列(三)——FTPserver之设计与实现
在实现了HTTPserver之后.本人打算再实现一个FTPserver. 因为FTP协议与HTTP一样都位于应用层,所以实现原理也类似. 在这里把实现的原理和源代码分享给大家. 首先须要明白的是FTP ...
- 使用SQLiteHelper创建数据库并插入数据 分类: H1_ANDROID 2013-11-05 22:44 1398人阅读 评论(0) 收藏
参考<疯狂android讲义>8.4节P424 1.获取SQLiteDatabase实例有2种方法,一是直接new SQLiteDatabase(),另一种使用SQLiteHelper.一 ...
- js进阶正则表达式方括号(方括号作用)(js正则是在双正斜杠之中:/[a-z]/g)
js进阶正则表达式方括号(方括号作用)(js正则是在双正斜杠之中:/[a-z]/g) 一.总结 方括号:范围 圆括号:选 大括号:数量 1.js正则是在双正斜杠之中: var reg2=/[a-z]/ ...
- php实现找链表中环的入口节点(画图、看评论)
php实现找链表中环的入口节点(画图.看评论) 一.总结 画图.看评论 二.php实现找链表中环的入口节点 题目描述: 一个链表中包含环,请找出该链表的环的入口结点. 三.代码 第一步,找环中相汇点. ...
- jquery获取选中select的文本,值等
jquery获取select选择的文本与值获取select :获取select 选中的 text : $("#ddlregtype").find("option:s ...
- [Java][web]利用Spring随时随地获得Request和Session
利用Spring随时随地获得Request和Session 一.准备工作: 在web.xml中加入 <listener> <listener-class> org.spring ...
- Android Snackbar使用方法及小技巧-design
Snackbar和Toast相似,都是为了给用户提供交互信息,Snackbar是固定在底部的,显示时从下往上滑出 要使用Snackbar,需要在项目的build.gradle中添加依赖 depende ...
- js进阶 10-10 可见伪类选择器和内容伪类选择器的作用
js进阶 10-10 可见伪类选择器和内容伪类选择器的作用 一.总结 一句话总结:分组来描述.内容伪类选择器就是 四个 包含.可见的伪类选择器就是可见和不可见.查找功能,也就是内容伪类选择器非常 ...