【算法】论平衡二叉树(AVL)的正确种植方法
平衡二叉树的由来
普通二叉搜索树的缺陷
get(根据key获取val) max(获取最大key), min(获取最小key) floor(对key向下取整) ceiling(对key向上取整) rank(获取给定key的排名) select(根据排名获得给定key)
put (插入键值对) delete(删除键值对)
BST的动态方法可能会修改二叉树的结构, 使其结点分布不均匀,使得在下一步的操作中, 静态方法和动态方法都变得更为低效。
插入的顺序影响二叉搜索树的构造
为什么二叉搜索树会变得低效?
插入和删除操作都可能降低未来操作的性能
什么是平衡二叉树
AVL和普通BST区别在于动态方法
平衡二叉树的监督机制
为每个结点设置并维护height属性
/*** @Author: HuWan Peng* @Date Created in 10:35 2017/12/29*/ public class AVL {Node root; // 根结点private class Node {int key,val;Node left,right;int height = 1; // 每个结点的高度属性public Node (int key, int val) {this.key = key;this.val = val;}}// 编写API方法 }
- 在插入结点时(put), 沿插入的路径更新结点的高度值(不一定会加1 !只是要重新计算)
- 在删除结点时(delete),沿删除的路径更新结点的高度值(不一定减1! 只是要重新计算)
- 在发现二叉树变得不平衡的时候, 通过“旋转”使其平衡, 这时候要更新相关结点的高度值(具体的我下面会详细讲)
/*** @description: 返回两个数中的最大值*/private int max (int a, int b) {return a>b ? a : b;}/*** @description: 获得当前结点的高度*/private int height (Node x) {if(x == null) return 0;return x.height;}// 下面的insert方法是简化后的代码public Node insert (Node x, int key, int val) {其他代码 。。。。insert(x.left, key, val); // 进行递归的插入x.height = max(height(x.left),height(x.right)) + 1; // 更新结点的height属性(沿着递归路径)return x;}
x.height = max(height(x.left),height(x.right)) + 1;
计算BF以监督平衡二叉树的状态
/*** @description: 获得平衡因子*/private int getBalance (Node x) {if(x == null) return 0;return height(x.left) - height(x.right);}
平衡二叉树的修正机制
左旋和右旋
/*** @description: 右旋方法*/private Node rotateRight (Node x) {Node y = x.left; // 取得x的左儿子x.left = y.right; // 将x左儿子的右儿子("拖油瓶"结点)链接到旋转后的x的左链接中y.right = x; // 调转x和它左儿子的父子关系,使x成为它原左儿子的右子树x.height = max(height(x.left),height(x.right)) + 1; // 更新并维护受影响结点的heighty.height = max(height(y.left),height(y.right)) + 1; // 更新并维护受影响结点的heightreturn y; // 将y返回}
/*** @description: 左旋方法*/private Node rotateLeft (Node x) {Node y = x.right; // 取得x的右儿子x.right = y.left; // 将x右儿子的左儿子("拖油瓶"结点)链接到旋转后的x的右链接中y.left = x; // 调转x和它右儿子的父子关系,使x成为它原右儿子的左子树x.height = max(height(x.left),height(x.right)) + 1; // 更新并维护受影响结点的heighty.height = max(height(y.left),height(y.right)) + 1; // 更新并维护受影响结点的heightreturn y; // 将y返回}
平衡化操作的四种情况
编写平衡化代码
/*** @description: 获得平衡因子*/private int getBalance (Node x) {if(x == null) return 0;return height(x.left) - height(x.right);}/*** @description: 平衡化操作: 检测当前结点是否失衡,若失衡则进行平衡化*/private Node reBalance (Node x) {int balanceFactor = getBalance(x);if(balanceFactor > 1&&getBalance(x.left)>0) { // LL型,进行单次右旋return rotateRight(x);}if(balanceFactor > 1&&getBalance(x.left)<=0) { //LR型 先左旋再右旋Node t = rotateLeft(x);return rotateRight(t);}if(balanceFactor < -1&&getBalance(x.right)<=0) {//RR型, 进行单次左旋return rotateLeft(x);}if(balanceFactor < -1&&getBalance(x.right)>0) {// RL型,先右旋再左旋Node t = rotateRight(x);return rotateLeft(t);}return x;}
AVL类的API编码
插入方法
/*** @description: 插入结点(键值对)*/public Node put (Node x, int key, int val) {if(x == null) return new Node(key, val); // 插入键值对if (key<x.key) x.left = put(x.left, key, val); // 向左子树递归插入else if(key>x.key) x.right = put(x.right,key, val); // 向右子树递归插入else x.val = val; // key已存在, 替换valx.height = max(height(x.left),height(x.right)) + 1; // 沿递归路径从下至上更新结点height属性x = reBalance(x); // 沿递归路径从下往上, 检测当前结点是否失衡,若失衡则进行平衡化return x;}
删除方法
/*** @description: 返回最小键*/private Node min (Node x) {if(x.left == null) return x; // 如果左儿子为空,则当前结点键为最小值,返回return min(x.left); // 如果左儿子不为空,则继续向左递归 }public int min () {if(root == null) return -1;return min(root).key;}/*** @description: 删除最小键的结点*/public Node deleteMin (Node x) {if(x.left==null) return x.right; // 如果当前结点左儿子空,则将右儿子返回给上一层递归的x.leftx.left = deleteMin(x.left);// 向左子树递归, 同时重置搜索路径上每个父结点指向左儿子的链接return x; // 当前结点不是min }public void deleteMin () {root = deleteMin(root);}/*** @description: 删除给定key的键值对*/private Node delete (int key,Node x) {if(x == null) return null;if (key<x.key) x.left = delete(key,x.left); // 向左子树查找键为key的结点else if (key>x.key) x.right = delete(key,x.right); // 向右子树查找键为key的结点else{// 结点已经被找到,就是当前的xif(x.left==null) return x.right; // 如果左子树为空,则将右子树赋给父节点的链接if(x.right==null) return x.left; // 如果右子树为空,则将左子树赋给父节点的链接Node inherit = min(x.right); // 取得结点x的继承结点inherit.right = deleteMin(x.right); // 将继承结点从原来位置删除,并重置继承结点右链接inherit.left = x.left; // 重置继承结点左链接x = inherit; // 将x替换为继承结点 }if(root == null) return root;x.height = max(height(x.left),height(x.right)) + 1; // 沿递归路径从下至上更新结点height属性x = reBalance(x); // 沿递归路径从下往上, 检测当前结点是否失衡,若失衡则进行平衡化return x;}public void delete (int key) {root = delete(key, root);}
测试AVL和BST的动态操作对二叉树结构的影响
/*** @description: 二叉树层序遍历*/private void levelIterator () {LinkedList <Node> queue = new LinkedList <Node>();Node current = null;int childSize = 0;int parentSize = 1;queue.offer(root);while(!queue.isEmpty()) {current = queue.poll();//出队队头元素并访问System.out.print(current.val +" ");if(current.left != null)//如果当前节点的左节点不为空入队 {queue.offer(current.left);childSize++;}if(current.right != null)//如果当前节点的右节点不为空,把右节点入队 {queue.offer(current.right);childSize++;}parentSize--;if (parentSize == 0){parentSize = childSize;childSize = 0;System.out.println("");}}}
public static void main(String [] args) {BST bst = new BST();bst.put(1,11);bst.put(2,22);bst.put(3,33);bst.put(4,44);bst.put(5,55);bst.put(6,66);bst.levelIterator();}
11 22 33 44 55 66
public static void main (String [] args) {AVL avl = new AVL();avl.put(1,11);avl.put(2,22);avl.put(3,33);avl.put(4,44);avl.put(5,55);avl.put(6,66);avl.levelIterator();}
44 22 55 11 33 66
全部代码
import java.util.LinkedList;/** * @Author: HuWan Peng * @Date Created in 10:35 2017/12/29 */ public class AVL {Node root; // 根结点private class Node {int key,val;Node left,right;int height = 1; // 每个结点的高度属性public Node (int key, int val) {this.key = key;this.val = val;}}/*** @description: 返回两个数中的最大值*/private int max (int a, int b) {return a>b ? a : b;}/*** @description: 获得当前结点的高度*/private int height (Node x) {if(x == null) return 0;return x.height;}/*** @description: 获得平衡因子*/private int getBalance (Node x) {if(x == null) return 0;return height(x.left) - height(x.right);}/*** @description: 右旋方法*/private Node rotateRight (Node x) {Node y = x.left; // 取得x的左儿子x.left = y.right; // 将x左儿子的右儿子("拖油瓶"结点)链接到旋转后的x的左链接中y.right = x; // 调转x和它左儿子的父子关系,使x成为它原左儿子的右子树x.height = max(height(x.left),height(x.right)) + 1; // 更新并维护受影响结点y.height = max(height(y.left),height(y.right)) + 1; // 更新并维护受影响结点return y; // 将y返回 }/*** @description: 左旋方法*/private Node rotateLeft (Node x) {Node y = x.right; // 取得x的右儿子x.right = y.left; // 将x右儿子的左儿子("拖油瓶"结点)链接到旋转后的x的右链接中y.left = x; // 调转x和它右儿子的父子关系,使x成为它原右儿子的左子树x.height = max(height(x.left),height(x.right)) + 1; // 更新并维护受影响结点y.height = max(height(y.left),height(y.right)) + 1; // 更新并维护受影响结点return y; // 将y返回 }/*** @description: 平衡化操作*/private Node reBalance (Node x) {int balanceFactor = getBalance(x);if(balanceFactor > 1&&getBalance(x.left)>0) { // LL型,进行单次右旋return rotateRight(x);}if(balanceFactor > 1&&getBalance(x.left)<=0) { //LR型 先左旋再右旋Node t = rotateLeft(x);return rotateRight(t);}if(balanceFactor < -1&&getBalance(x.right)<=0) {//RR型, 进行单次左旋return rotateLeft(x);}if(balanceFactor < -1&&getBalance(x.right)>0) {// RL型,先右旋再左旋Node t = rotateRight(x);return rotateLeft(t);}return x;}/*** @description: 插入结点(键值对)*/public Node put (Node x, int key, int val) {if(x == null) return new Node(key, val); // 插入键值对if (key<x.key) x.left = put(x.left, key, val); // 向左子树递归插入else if(key>x.key) x.right = put(x.right,key, val); // 向右子树递归插入else x.val = val; // key已存在, 替换valx.height = max(height(x.left),height(x.right)) + 1; // 沿递归路径从下至上更新结点height属性x = reBalance(x); // 沿递归路径从下往上, 检测当前结点是否失衡,若失衡则进行平衡化return x;}public void put (int key,int val) {root = put(root,key,val);}/*** @description: 返回最小键*/private Node min (Node x) {if(x.left == null) return x; // 如果左儿子为空,则当前结点键为最小值,返回return min(x.left); // 如果左儿子不为空,则继续向左递归 }public int min () {if(root == null) return -1;return min(root).key;}/*** @description: 删除最小键的结点*/public Node deleteMin (Node x) {if(x.left==null) return x.right; // 如果当前结点左儿子空,则将右儿子返回给上一层递归的x.leftx.left = deleteMin(x.left);// 向左子树递归, 同时重置搜索路径上每个父结点指向左儿子的链接return x; // 当前结点不是min }public void deleteMin () {root = deleteMin(root);}/*** @description: 删除给定key的键值对*/private Node delete (int key,Node x) {if(x == null) return null;if (key<x.key) x.left = delete(key,x.left); // 向左子树查找键为key的结点else if (key>x.key) x.right = delete(key,x.right); // 向右子树查找键为key的结点else{// 结点已经被找到,就是当前的xif(x.left==null) return x.right; // 如果左子树为空,则将右子树赋给父节点的链接if(x.right==null) return x.left; // 如果右子树为空,则将左子树赋给父节点的链接Node inherit = min(x.right); // 取得结点x的继承结点inherit.right = deleteMin(x.right); // 将继承结点从原来位置删除,并重置继承结点右链接inherit.left = x.left; // 重置继承结点左链接x = inherit; // 将x替换为继承结点 }if(root == null) return root;x.height = max(height(x.left),height(x.right)) + 1; // 沿递归路径从下至上更新结点height属性x = reBalance(x); // 沿递归路径从下往上, 检测当前结点是否失衡,若失衡则进行平衡化return x;}public void delete (int key) {root = delete(key, root);}private void levelIterator () {LinkedList <Node> queue = new LinkedList <Node>();Node current = null;int childSize = 0;int parentSize = 1;queue.offer(root);while(!queue.isEmpty()) {current = queue.poll();//出队队头元素并访问System.out.print(current.val +"-->");if(current.left != null)//如果当前节点的左节点不为空入队 {queue.offer(current.left);childSize++;}if(current.right != null)//如果当前节点的右节点不为空,把右节点入队 {queue.offer(current.right);childSize++;}parentSize--;if (parentSize == 0){parentSize = childSize;childSize = 0;System.out.println("");}}}public static void main (String [] args) {AVL avl = new AVL();avl.put(1,11);avl.put(2,22);avl.put(3,33);avl.put(4,44);avl.put(5,55);avl.put(6,66);avl.levelIterator();} }
【算法】论平衡二叉树(AVL)的正确种植方法相关推荐
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现中的事实代码
普通二叉查找树的问题 在开篇,我们提到过,普通二叉树(二叉查找树)在操作的时间复杂度上不一定遵循O(㏒n),也有可能是O(n),这是为什么呢?在上一篇中,我们明明插入都按照一定规则比较的呀,其实那 ...
- 【算法】平衡二叉树 Avl 树
文章目录 1.概述 1.1 案例 1.2 平衡二叉树 1.3 案例 2.code 2.1 获取当前树的高度 2.2 左子树 右子树的高度 2.3 左旋转 2.4 左旋时机 2.5 右旋转 2.5.1 ...
- 平衡查找树C语言程序,C语言数据结构之平衡二叉树(AVL树)实现方法示例
本文实例讲述了C语言数据结构之平衡二叉树(AVL树)实现方法.分享给大家供大家参考,具体如下: AVL树是每个结点的左子树和右子树的高度最多差1的二叉查找树. 要维持这个树,必须在插入和删除的时候都检 ...
- [转]C#与数据结构--树论--平衡二叉树(AVL Tree)
C#与数据结构--树论--平衡二叉树(AVL Tree) http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html 介绍 我们知道在二 ...
- matlab碎纸拼接相似函数,基于蒙特卡洛算法构建能量函数的碎纸图片拼接方法
基于蒙特卡洛算法构建能量函数的碎纸图片拼接方法 [专利摘要]本发明提供了一种基于蒙特卡洛算法构建能量函数的碎纸图片拼接方法,主要涉及双面打印文件的拼接及复原问题,通常由于图片较多,信息量较大,故通常为 ...
- 各种神经网络优化算法:从梯度下降到Adam方法
在调整模型更新权重和偏差参数的方式时,你是否考虑过哪种优化算法能使模型产生更好且更快的效果?应该用梯度下降,随机梯度下降,还是Adam方法? 这篇文章介绍了不同优化算法之间的主要区别,以及如何选择最佳 ...
- 平衡二叉树平衡因子怎么计算_数据结构PHP 平衡二叉树(AVL)的平衡原理
这篇文章主要介绍一下 平衡二叉树(AVL),对于 二分搜索树 来说,如果树上的 元素 是顺序 添加的,会导致数据退化成一个 链表,这样就会造成很严重的性能问题,此时就需要在 二分搜索树 的基础上,保证 ...
- 数据结构与算法——图解平衡二叉树及代码实现
平衡二叉树介绍 平衡二叉树,是一种二叉排序树,其中每一个节点的左子树和右子树的高度差最多等于1.由3位科学家共同发明,用他们首字母命名 又被称为AVL树.从平衡二叉树的名称,你也可以体会到它是一种高度 ...
- [ 数据结构 ] 平衡二叉树(AVL)--------左旋、右旋、双旋
0 引出 数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在 回顾:二叉搜索树 左子树全部为空,从形式上看,更像一个单链表. 插入速度没有影响 查询速度明显降低(因为需 ...
最新文章
- 一天搞定CSS(扩展):CSS Hack
- sharepoint数据库研究
- 张小娴的文章,喜欢的,贴来存档
- asp.net core 教程(六)-中间件
- TypeScript入门教程 之 Let 关键字
- python里怎么读取文件-python怎么读取文本文件
- 自动执行任务_中小企业如何做运维自动化?
- linux 配置mq队列,Apache ActiveMQ 消息队列安装与配置
- 魔鬼训练Day1作业
- 基础线性代数知识点总结与回顾(四):线性空间
- 什么是 JScript?
- 融合多头注意力机制的网络恶意流量检测
- SSM+微信小程序网易云音乐设计与实现 毕业设计-附源码261620
- Linux下Netfilter创建自己的Hook,让数据包可以发送到用户层,然后统计节点负载信息
- team网卡绑定以及模式切换
- 美国房价仍在上涨,但购房需求今年首次低于2020年水平
- koolcenter官改固件软件中心无法离线安装插件
- PTA甲级 1097 Deduplication on a Linked List (25分)-链表处理
- 深圳码农买房记4:踩点篇
- Spark Nightly Builds