Java 平衡二叉树之单旋(左旋,右旋)与双旋
1.平衡二叉树
平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。
具有以下特点:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
首先要明确的是,平衡二叉树是一棵二叉排序树,它的出现是为了解决普通二叉排序树(普通二叉排序树)不平衡的问题。如图,在插入结点之前首先要查找插入位置,假如要在5结点后插入,普通二叉排序树需要比较五次,而平衡二叉树只需要比较三次。假如结点规模进一步加大,效率提升也会更明显。
2.二叉树到平衡二叉树:单旋(左旋,右旋)与双旋
2.1单旋转:
左旋(当rightHeight-leftHeight>1)
旋转后
根结点:最新的根结点的右孩子为原根结点的右孩子
左子树:左子树的根即为原来的根,左子树的左子树即为原来的左子树,左子树的 右子树为原来右子树的左子树
右子树:右子树为原根结点右孩子的右子树
右旋(当rightHeight-leftHeight>1)
旋转后
根结点:最新的根结点的左孩子为原根结点的右孩子
左子树:左子树为原根结点左孩子的左子树
右子树:右子树的根即为原来的根,右子树的右子树即为原来的右子树,右子树的左子树为原来左子树的右子树
2.2.双旋转:
我们会发现,对于有些树,单旋是无法解决问题的:
问题分析:
当符合右旋的条件时(左旋同理),如果它的左子树的右子树高度大于它的右子树高度,
假设左子树的高度为x,右子树的高度为(x-2),左子树的左子树高度(x-2),左子树的右子树高度(x-1)
右旋后:左子树的高度为x-2,右子树的高度x((x-1)+1=x),仍不满足平衡二叉树
解决:先对这个结点的左节点进行左旋转,再对当前结点进行右旋转即可。
3.代码实现
关于单旋(左旋,右旋)与双旋的处理都在添加结点的功能后完成,即添加一个结点,就对新的树进行判断是否需要"平衡"。
当前结点的高度与左右结点的高度
//求该结点的高度
public int getHeight(){return Math.max(this.left==null?0:this.left.getHeight(),this.right==null?0:this.right.getHeight())+1; //妙
}//左节点的高度
public int getLeftHeight(){if(this.left==null){return 0;}else{return this.left.getHeight();}
}
//右节点的高度
public int getRightHeight(){if(this.right==null){return 0;}else{return this.right.getHeight();}
}
左旋
//左旋
public void leftRotate(){ //创建新的左子树//创建新的结点,以当前根结点的值AVLNode node=new AVLNode(this.value);//把新的结点的左子树设置成当前结点的左子树node.left=this.left;//把新的结点的右子树设置成带你过去结点的右子树的左子树node.right=this.right.left;//创建新的根结点//把当前结点的值替换成右子结点的值this.value=this.right.value;//(根结点+右子树)//把当前结点的右子树设置成当前结点右子树的右子树this.right=this.right.right;//(根结点+右子树+左子树)//把当前结点的左子树(左子结点)设置成新的结点this.left=node;
}
右旋
//右旋
public void rightRotate(){ //创建新的右子树//创建新的结点,以当前根结点的值AVLNode node=new AVLNode(this.value);//把新的结点的右子树设置成当前结点的右子树node.right=this.right;//把新的结点的左子树设置成带你过去结点的左子树的右子树node.left=this.left.right;//创建新的根结点//把当前结点的值替换成右子结点的值this.value=this.left.value;//(根结点+左子树)//把当前结点的左子树设置成当前结点左子树的左子树this.left=this.left.left;//(根结点+右子树+左子树)//把当前结点的左子树(左子结点)设置成新的结点this.right=node;
}
向二叉排序树添加结点(平衡二叉树的创建)
/*** 向二叉排序树添加结点* @param node*/
public void add(AVLNode node){if(node==null){return;}if(node.value<this.value){if(this.left==null){this.setLeft(node);}else{this.left.add(node);}}else{if(this.right==null){this.setRight(node);}else{this.right.add(node);}}//当添加完一个结点后,如果:(右子树的高度-左子树的高度)>1,左旋转if(this.getRightHeight()-this.getLeftHeight()>1){//如果它的右子树的左子树高度大于它的左子树高度,双旋转if(this.right!=null&&this.right.getLeftHeight()>this.getLeftHeight()){//先对这个结点的右节点进行右旋转this.right.rightRotate();}//再对当前结点进行左旋转即可。this.leftRotate();}//当添加完一个结点后,如果:(左子树的高度-右子树的高度)>1,右旋转else if(this.getLeftHeight()-this.getRightHeight()>1){//如果它的左子树的右子树高度大于它的右子树高度,双旋转if(this.left!=null&&this.left.getRightHeight()>this.getRightHeight()){//先对这个结点的左节点进行左旋转this.left.leftRotate();}//再对当前结点进行右旋转即可。this.rightRotate();}else{return;}
}
4.完整代码
package tree;
/*** 平衡二叉树* 1.左旋* 2,右旋* 3.双旋* @author BayMax**/
public class AVLTreeDemo {public static void main(String[] args) {AVLTree avlt=new AVLTree();int []arr={10,7,11,6,8,9};//循环添加结点到二叉排序树for(int tmp:arr){avlt.add(new AVLNode(tmp));}//中序遍历avlt.infixOrder();//测试结点的高度System.out.println(avlt.getRoot()+"\t"+avlt.getHeight());for(int tmp:arr){System.out.println(tmp+" "+avlt.search(tmp).getHeight());}}
}class AVLTree{private AVLNode root;public AVLTree() {super();// TODO Auto-generated constructor stub}public AVLTree(AVLNode root) {super();this.root = root;}public AVLNode getRoot() {return root;}public void setRoot(AVLNode root) {this.root = root;}//添加结点的方法public void add(AVLNode node){if(root==null){root=node;}else{root.add(node);}}//中序遍历public void infixOrder(){if(root==null){System.out.println("该二叉树为空");}else{root.infixOrder();}}//查找结点public AVLNode search(int value){if(root==null){System.out.println("该二叉树为空");}return root.search(value);}//树的高度public int getHeight(){if(root==null){return 0;}return root.getHeight();}
}class AVLNode{private int value;private AVLNode left;private AVLNode right;public AVLNode() {super();}public AVLNode(int value) {super();this.value = value;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public AVLNode getLeft() {return left;}public void setLeft(AVLNode left) {this.left = left;}public AVLNode getRight() {return right;}public void setRight(AVLNode right) {this.right = right;}@Overridepublic String toString() {return "BSTNode [value=" + value + "]";}/*** 向二叉排序树添加结点* @param node*/public void add(AVLNode node){if(node==null){return;}if(node.value<this.value){if(this.left==null){this.setLeft(node);}else{this.left.add(node);}}else{if(this.right==null){this.setRight(node);}else{this.right.add(node);}}//当添加完一个结点后,如果:(右子树的高度-左子树的高度)>1,左旋转if(this.getRightHeight()-this.getLeftHeight()>1){//如果它的右子树的左子树高度大于它的左子树高度,双旋转if(this.right!=null&&this.right.getLeftHeight()>this.getLeftHeight()){//先对这个结点的右节点进行右旋转this.right.rightRotate();}//再对当前结点进行左旋转即可。this.leftRotate();}//当添加完一个结点后,如果:(左子树的高度-右子树的高度)>1,右旋转else if(this.getLeftHeight()-this.getRightHeight()>1){//如果它的左子树的右子树高度大于它的右子树高度,双旋转if(this.left!=null&&this.left.getRightHeight()>this.getRightHeight()){//先对这个结点的左节点进行左旋转this.left.leftRotate();}//再对当前结点进行右旋转即可。this.rightRotate();}else{return;}}//中序遍历public void infixOrder(){if(this.getLeft()!=null){this.left.infixOrder();}System.out.println(this);if(this.getRight()!=null){this.right.infixOrder();}}//查找要删除结点public AVLNode search(int value){if(this.value==value){return this;}//左子树查找if(value<this.value){if(this.left==null){return null;}else{return this.left.search(value);}}//右子树查找else{if(this.right==null){return null;}else{return this.right.search(value);}}}//求该结点的高度public int getHeight(){return Math.max(this.left==null?0:this.left.getHeight(),this.right==null?0:this.right.getHeight())+1; //妙}//左节点的高度public int getLeftHeight(){if(this.left==null){return 0;}else{return this.left.getHeight();}}//右节点的高度public int getRightHeight(){if(this.right==null){return 0;}else{return this.right.getHeight();}}//左旋public void leftRotate(){ //创建新的左子树//创建新的结点,以当前根结点的值AVLNode node=new AVLNode(this.value);//把新的结点的左子树设置成当前结点的左子树node.left=this.left;//把新的结点的右子树设置成带你过去结点的右子树的左子树node.right=this.right.left;//创建新的根结点//把当前结点的值替换成右子结点的值this.value=this.right.value;//(根结点+右子树)//把当前结点的右子树设置成当前结点右子树的右子树this.right=this.right.right;//(根结点+右子树+左子树)//把当前结点的左子树(左子结点)设置成新的结点this.left=node;}//右旋public void rightRotate(){ //创建新的右子树//创建新的结点,以当前根结点的值AVLNode node=new AVLNode(this.value);//把新的结点的右子树设置成当前结点的右子树node.right=this.right;//把新的结点的左子树设置成带你过去结点的左子树的右子树node.left=this.left.right;//创建新的根结点//把当前结点的值替换成右子结点的值this.value=this.left.value;//(根结点+左子树)//把当前结点的左子树设置成当前结点左子树的左子树this.left=this.left.left;//(根结点+右子树+左子树)//把当前结点的左子树(左子结点)设置成新的结点this.right=node;}
}
Java 平衡二叉树之单旋(左旋,右旋)与双旋相关推荐
- 平衡二叉树的左旋右旋详解 看不懂你打我
平衡二叉树的左旋右旋 看不懂你打我 左旋右旋的操作 为什么要左旋右旋 左旋右旋能保持排序二叉排序树的性质吗 下次写平衡二叉树的LL.RR.LR.RL. 左旋右旋的操作 1.左旋:对X节点左旋,即以X的 ...
- 搞懂平衡二叉树的左旋右旋双旋(Java实现)
刚看到韩顺平老师的数据结构与算法对于平衡二叉树的讲解(最后会附上地址),有如下理解,希望能帮助大家!哪里需要改正的欢迎指正! 平衡二叉树:一种二叉排序树(BST Binary Sort Tree)的升 ...
- avl树左旋右旋的理解
一直没搞懂非平衡二叉树变平衡二叉树时左旋右旋,今天下定决心搞懂,然后在众多博客中终于找到了这样一篇,非常形象,记录如下: AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大 ...
- 红黑树的基本原理和左旋右旋
红黑树: 平衡二叉树 1.根节点的左边都是比根节点小的,右边都是比根节点大的 2.不能有两个连续的红色,可以有两个连续的黑色 3.首先插入时默认都是红色 4.当某一个节点为红色,他的父节点和叔叔节点也 ...
- 左旋右旋问题一次搞定!!!
左旋右旋问题的解决 编程思想 1.在左右旋函数中实现该函数功能首先要想好如何存放移位后的字符元素 2.左旋时将字符串首元素赋给临时变量tmp而后将字符串元素依次前移一位 3.将tmp的值再赋给字符串的 ...
- HashMap 数据结构之红黑树, 红黑树在什么时候左旋 右旋 如何旋转
树结构是数据结构中最经典最常用的结构之一,也是面试中常问的面试题,最近学习了一下红黑树的知识,记录整理一下 文章目录 一.红黑树的特征 二.变色左旋和右旋 1.变色规则 2.左旋 3.右旋 总结 前言 ...
- 字符串左旋右旋——三步旋转法和移相法
题目:实现一个函数,可以左旋字符串中的k个字符. AABCD左旋一个字符得到ABCDA AABCD左旋两个字符得到BCDAA 方法一:三步旋转法 左旋程序思路:首先根据画图得知左旋后的结果,然后在分析 ...
- nyoj202 红黑树 (二叉树的左旋右旋)
题目202 题目信息 运行结果 本题排行 讨论区 红黑树 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 什么是红黑树呢?顾名思义,跟枣树类似,红黑树是一种叶子是黑色果子 ...
- 详解红黑树之左旋右旋
为什么要左旋右旋? 为了使得左右子树的高度差在一定范围内,需要通过旋转调整,这样就可以保持平稳的搜索效率 左旋: 步骤 1.设原来E的父节点是father,那么左旋之后需要改变的是: 2.S和fath ...
- 字符串的左旋右旋问题(C语言实现,三种方法求解)
字符串左旋右旋问题其实是同理的,下边以左旋为例: 方法一 思路:左旋一次就是将整个字符串向左移一个字符,第一个字符(arr[0])移动到最右侧.这样循环操作左旋次数就是最终左旋结果,如上图所示. 实现 ...
最新文章
- 刚刚,马斯克再次创造航天历史!SpaceX首次载人发射任务成功
- linux双机热备 oracle,oracle for linux双机热备实战
- 8s pod 查看 的yaml_Kubernetes入门到实战(五)深入浅出详解Pod
- Matlab 2022a 安装教程(手把手式教程 超简单)
- No plugin found for prefix ‘compile‘ in the current project
- 《Access 2007开发指南(修订版)》一一1.5 什么是数据库对象
- java之解析DNS的SRV记录
- HTTP状态码表格汇总
- .NET 垃圾回收与内存泄漏
- lodash按需引入
- 关于opencv4.5.3读取视频失败问题
- 《离散数学》题库大全及答案
- smarty3.1.30 模板引擎的使用
- 百度开放平台Demo提示“Key验证失败...”的问题
- ESD(静电释放)下半部分
- JavaScript制作游戏摇杆方向盘
- c语言一本书的页码从自然数1开始顺序编码,算法设计与分析 1-1 统计数字问题(C语言版)...
- 新生儿小名大全:农历三月出生的女孩小名
- 使用计算机录制声音10,Win10怎么录制电脑内部声音 Win10电脑自身录音教程
- Sort_1000pics数据集利用CNN实现图像分类
热门文章
- BIM与三维GIS结合应用
- 禅意茶艺茶道茶文化PPT模板
- 基于 web 的单视图三维重建可视化系统
- QT课程设计:基于QT的图像处理程序
- 背书小程序正式上线啦
- pve 不订阅更新_这款曾霸榜TapTap的游戏,创造了不一样的自走棋玩法
- 计算机控制6路模拟量输出,工业级开关量远程无线收发模块|无线开关量6路输入6路输出控制器|4-20ma模拟量无线通信模块...
- 蓝桥杯 算法训练 - 连续正整数的和 78这个数可以表示为连续正整数的和,1+2+3,18+19+20+21,25+26+27。 输入一个正整数 n(<=10000) 输出 m 行(n有m
- 《Frustum PointNets for 3D Object Detection from RGB-D Data》中文翻译
- 机器学习 | MATLAB实现BP神经网络newff参数设定(上)