手写AVL平衡二叉搜索树
手写AVL平衡二叉搜索树
二叉搜索树的局限性
先说一下什么是二叉搜索树,二叉树每个节点只有两个节点,二叉搜索树的每个左子节点的值小于其父节点的值,每个右子节点的值大于其左子节点的值。如下图:
二叉搜索树,顾名思义,它的搜索效率很高,可以达到O(logn)。但这是理想状况下的,即上图所示。实际上,由于插入顺序的原因,形成的二叉搜索树并不会像上图这样“工整”,最坏的情况的下,甚至可能会退化成链表了,如下图:
这显然不是我们想要看的结果,那么我们必须要引入一套机制来避免这种事情的发生,也就是让二叉搜索树带上平衡条件。
AVL平衡二叉搜索树
几个基本概念
叶子节点:既没有左子节点,也没有右左子节点的节点就是叶子节点。
树的高度:叶子节点的高度为1,空节点的高度是-1,父节点的高度是其两个子树较高一棵子树的高度加一。
平衡条件:每一个节点的左子树与右子树的高度差不超过1。
核心思想
因为AVL平衡二叉搜索树,父节点的两颗子树的高度差不能超过1。在AVL平衡二叉树种,采用旋转的机制来使不满足平衡条件的二叉树重新回到满足平衡条件的状态。在二叉搜索树中,需要被平衡的情况可以分为两大类总共四种情况,
- 单旋转
- 左旋转
- 右旋转
- 双旋转
- 先左旋,再右旋
- 先右旋,再左旋
如下图所示:
通过图片的形式我们很容易就可以写出使二叉树回到满足平衡条件的代码
// 右旋转
public TreeNode rightRotate(TreeNode root) {
TreeNode temp1 = root.left;
TreeNode temp2 = temp1.right;
temp1.right = root;
root.left = temp2;
return temp1;
}
// 左旋转
public TreeNode leftRotate(TreeNode root) {
TreeNode temp1 = root.right;
TreeNode temp2 = temp1.left;
temp1.left = root;
root.right = temp2;
return temp1;
}
// 先右后左
public TreeNode rightLeftRotate(TreeNode root) {
root.right = rightRotate(root.right);
return leftRotate(root);
}
// 先左后右
public TreeNode leftRightRotate(TreeNode root) {
root.left = leftRotate(root.left);
return rightRotate(root);
}
我们必须再每一次插入节点后判断树是否需要平衡,也就是是否会出现两颗子树的高度差超过1的情况,首先编写一个可以计算出传入节点 高度的函数。
public int height(TreeNode root) {
if (root == null) {
return -1;
}
if (root.left == null && root.right == null) {
return 0;
}
return Math.max(height(root.right), height(root.left));
}
有了这个函数,我们就不仅可以判断是否出现需要平衡的情况,还可以判断需要平衡的情况是四种情况种的哪一种。
public TreeNode balance(TreeNode root) {
int l = height(root.left);
int r = height(root.right);
if (l - r >= 2) {
// rightRotate
if (height(root.left.left) - height(root.left.right) >= 1) {
// rightRotate
root = rightRotate(root);
} else if (height(root.left.right) - height(root.left.left) >= 1) {
// leftRightRotate
root = leftRightRotate(root);
}
} else if (r - l >= 2) {
// leftRotate
if (height(root.right.right) - height(root.right.left) >= 1) {
// leftRotate
root = leftRotate(root);
} else if (height(root.right.left) - height(root.right.right) >= 1){
root = rightLeftRotate(root);
}
}
return root;
}
以上就是AVL平衡二叉搜索树的精髓,并且已经用代码实现了。
完整代码
这是我完善后的功能相对完整的AVL二叉搜索平衡树。
class TreeNode {
int value;
TreeNode left;
TreeNode right;
int height;
public TreeNode(int value) {
this.value = value;
}
}
public class AVLBinarySearchTree {
public static void main(String[] args) {
AVLBinarySearchTree a = new AVLBinarySearchTree();
for (int i = 0; i < 10; i++) {
a.insert(i);
}
}
private TreeNode root;
private static final int ALLOWED_IMBALANCE = 1;
// 删除元素
public void remove(int value) {
root = remove(value, root);
}
// 检查是否包含某一元素,包含则返回该节点,不包含则返回null
public TreeNode contain(int value) {
TreeNode temp = root;
if (temp == null) {
return temp;
}
while (temp.value != value) {
if (value > temp.value) {
temp = temp.right;
} else if (value < temp.value) {
temp = temp.left;
}
}
return temp;
}
// 删除指定子树上的指定元素
private TreeNode remove(int value, TreeNode abn) {
if (abn == null) {
return abn;
}
if (value > abn.value) {
abn.right = remove(value, abn.right);
} else if (value < abn.value) {
abn.left = remove(value, abn.left);
} else {
if (abn.right == null && abn.left == null) {
abn = null;
return abn;
} else if (abn.right != null) {
abn.value = findMin(abn.right).value;
abn.right = remove(abn.value, abn.right);
} else {
abn.value = findMax(abn.left).value;
abn.left = remove(abn.value, abn.left);
}
}
return balance(abn);
}
// 找到指定子树最大值
private TreeNode findMax(TreeNode abn) {
if (abn == null) {
return null;
}
TreeNode temp = abn;
while (temp.right != null) {
temp = temp.right;
}
return temp;
}
// 找到指定子树最小值
private TreeNode findMin(TreeNode abn) {
if (abn == null) {
return null;
}
TreeNode temp = abn;
while (temp.left != null) {
temp = temp.left;
}
return temp;
}
// 插入节点
public void insert(int value) {
root = insert(value, root);
}
// 计算节点高度
private int height(TreeNode abn) {
if (abn == null) {
return -1;
}
return abn.height;
}
// 树的高度
public int height() {
return height(root);
}
// 插入节点
private TreeNode insert(int value, TreeNode abn) {
if (abn == null) {
return new TreeNode(value);
}
if (value > abn.value) {
abn.right = insert(value, abn.right);
} else if (value < abn.value) {
abn.left = insert(value, abn.left);
}
return balance(abn);
}
// 平衡不平衡的树
private TreeNode balance(TreeNode abn) {
if (height(abn.left) - height(abn.right) > ALLOWED_IMBALANCE) {
if (height(abn.left.left) >= height(abn.left.right)) {
abn = leftSingleRotate(abn);
} else if (height(abn.left.left) < height(abn.left.right)) {
abn = leftDoubleRotate(abn);
}
} else if (height(abn.right) - height(abn.left) > ALLOWED_IMBALANCE) {
if (height(abn.right.right) >= height(abn.right.left)) {
abn = rightSingleRotate(abn);
} else {
abn = rightDoubleRotate(abn);
}
}
abn.height = Math.max(height(abn.left), height(abn.right)) + 1;
return abn;
}
// 右单旋转
private TreeNode rightSingleRotate(TreeNode abn) {
TreeNode temp = abn;
abn = abn.right;
temp.right = abn.left;
abn.left = temp;
temp.height = Math.max(height(temp.right), height(temp.left)) + 1;
abn.height = Math.max(height(abn.right), temp.height) + 1;
return abn;
}
// 左单旋转
private TreeNode leftSingleRotate(TreeNode abn) {
TreeNode temp = abn;
abn = abn.left;
temp.left = abn.right;
abn.right = temp;
temp.height = Math.max(height(temp.right), height(temp.left)) + 1;
abn.height = Math.max(height(abn.right), temp.height) + 1;
return abn;
}
// 右双旋转
private TreeNode rightDoubleRotate(TreeNode abn) {
abn.right = leftSingleRotate(abn.right);
return rightSingleRotate(abn);
}
// 左双旋转
private TreeNode leftDoubleRotate(TreeNode abn) {
abn.left = rightSingleRotate(abn.left);
return leftSingleRotate(abn);
}
}
手写AVL平衡二叉搜索树的更多相关文章
- 二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树
1.二叉搜索树 1.1定义 是一棵二叉树,每个节点一定大于等于其左子树中每一个节点,小于等于其右子树每一个节点 1.2插入节点 从根节点开始向下找到合适的位置插入成为叶子结点即可:在向下遍历时,如果要 ...
- AVL平衡二叉搜索树原理及各项操作编程实现
C语言版 #include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Po ...
- 【算法学习】AVL平衡二叉搜索树原理及各项操作编程实现(C语言)
#include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Positio ...
- 看动画学算法之:平衡二叉搜索树AVL Tree
目录 简介 AVL的特性 AVL的构建 AVL的搜索 AVL的插入 AVL的删除 简介 平衡二叉搜索树是一种特殊的二叉搜索树.为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜 ...
- convert sorted list to binary search tree(将有序链表转成平衡二叉搜索树)
Given a singly linked list where elements are sorted in ascending order, convert it to a height bala ...
- 算法:非平衡二叉搜索树(UnBalanced Binary Search Tree)
背景 很多场景下都需要将元素存储到已排序的集合中.用数组来存储,搜索效率非常高: O(log n),但是插入效率比较低:O(n).用链表来存储,插入效率和搜索效率都比较低:O(n).如何能提供插入和搜 ...
- LeetCode 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树
第108题 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定有序数组: [-10 ...
- 算法进阶面试题04——平衡二叉搜索树、AVL/红黑/SB树、删除和调整平衡的方法、输出大楼轮廓、累加和等于num的最长数组、滴滴Xor
接着第三课的内容和讲了第四课的部分内容 1.介绍二叉搜索树 在二叉树上,何为一个节点的后继节点? 何为搜索二叉树? 如何实现搜索二叉树的查找?插入?删除? 二叉树的概念上衍生出的. 任何一个节点,左比 ...
- 【数据结构与算法Python版学习笔记】树——平衡二叉搜索树(AVL树)
定义 能够在key插入时一直保持平衡的二叉查找树: AVL树 利用AVL树实现ADT Map, 基本上与BST的实现相同,不同之处仅在于二叉树的生成与维护过程 平衡因子 AVL树的实现中, 需要对每个 ...
随机推荐
- js 时间戳转为日期
1 function time(){ //页面时间戳转换成时间 2 $(".time").each(function(){ 3 var time = $(this).text(); ...
- 使用C#winform编写渗透测试工具--Web指纹识别
使用C#winform编写渗透测试工具--web指纹识别 本篇文章主要介绍使用C#winform编写渗透测试工具--Web指纹识别.在渗透测试中,web指纹识别是信息收集关键的一步,通常是使用各种工具 ...
- 探索HashMap源码 一行一行解析 jdk1.7版本
今天我们来说一说,HashMap的源码到底是个什么? 面试大厂这方面一定会经常问到,很重要的.以jdk1.7 为标准 先带着大家过一遍 是由数组.链表组成 , 数组的优点是:每个元素有对应下标, ...
- 原生ajax练习-post&xml
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 史上最详细的Android消息机制源码解析
本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...
- silky微服务简介
代理主机 silky微服务定义了三种类型的代理主机,开发者可以根据需要选择合适的silky代理主机托管微服务应用.代理主机定义了一个Startup模块,该模块给出了使用该种类型主机所必须依赖的模块. ...
- 易语言效率与C++究竟差多少(质数和计算)
文本首发bIlibili,cnblogs为作者补发,如在其他平台看见本文,均为经允许的盗窃 易语言作为款主打 中文 易用 编程的开发软件.但是很多人都在批评易语言的效率. 我们今天通过 质数和计算 来 ...
- CentOS的crond系统定时服务
crond 服务管理 [root@node01 ~]# service crond start (启动服务) [root@node01 ~]# service crond stop ...
- Install Redmine on Virtual Machine with Vagrant
Initialize VM: chad@typcserver ~/docs/vagrant-prj $ vagrant --version Vagrant 1.4.3 chad@typcserver ...
- SpringBoot开发二十二-统一处理异常
需求介绍 首先服务端分为三层:表现层,业务层,数据层. 请求过来先到表现层,表现层调用业务层,然后业务层调用数据层. 那么数据层出现异常它会抛出异常,那异常肯定是抛给调用者也就是业务层,那么业务层会再 ...