手写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平衡二叉搜索树的更多相关文章

  1. 二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树

    1.二叉搜索树 1.1定义 是一棵二叉树,每个节点一定大于等于其左子树中每一个节点,小于等于其右子树每一个节点 1.2插入节点 从根节点开始向下找到合适的位置插入成为叶子结点即可:在向下遍历时,如果要 ...

  2. AVL平衡二叉搜索树原理及各项操作编程实现

    C语言版 #include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Po ...

  3. 【算法学习】AVL平衡二叉搜索树原理及各项操作编程实现(C语言)

    #include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Positio ...

  4. 看动画学算法之:平衡二叉搜索树AVL Tree

    目录 简介 AVL的特性 AVL的构建 AVL的搜索 AVL的插入 AVL的删除 简介 平衡二叉搜索树是一种特殊的二叉搜索树.为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜 ...

  5. 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 ...

  6. 算法:非平衡二叉搜索树(UnBalanced Binary Search Tree)

    背景 很多场景下都需要将元素存储到已排序的集合中.用数组来存储,搜索效率非常高: O(log n),但是插入效率比较低:O(n).用链表来存储,插入效率和搜索效率都比较低:O(n).如何能提供插入和搜 ...

  7. LeetCode 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树

    第108题 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定有序数组: [-10 ...

  8. 算法进阶面试题04——平衡二叉搜索树、AVL/红黑/SB树、删除和调整平衡的方法、输出大楼轮廓、累加和等于num的最长数组、滴滴Xor

    接着第三课的内容和讲了第四课的部分内容 1.介绍二叉搜索树 在二叉树上,何为一个节点的后继节点? 何为搜索二叉树? 如何实现搜索二叉树的查找?插入?删除? 二叉树的概念上衍生出的. 任何一个节点,左比 ...

  9. 【数据结构与算法Python版学习笔记】树——平衡二叉搜索树(AVL树)

    定义 能够在key插入时一直保持平衡的二叉查找树: AVL树 利用AVL树实现ADT Map, 基本上与BST的实现相同,不同之处仅在于二叉树的生成与维护过程 平衡因子 AVL树的实现中, 需要对每个 ...

随机推荐

  1. Tomcat7+ 弱口令 && 后台getshell漏洞

    打开tomcat管理页面http://192.168.49.2:8080/manager/html,输入弱密码tomcat:tomcat,即可访问后台 先将jsp大马压缩为zip,再将zip后缀改名为 ...

  2. 【译】使用 Source Link 提高调试效率

    有多少次你在调试器中追踪一个缺陷,通过代码,观察局部变量的值改变,当你碰壁--不是你所期待的值和你不能进入的方法,因为它来自类库或 .NET 框架本身:或者您设置了一个条件断点,等待检查某个值是如何设 ...

  3. Js实现随机某个li样式增加

    一.首先引入jquery  cdn   二.基础样式 三.目的 为了使随机某个li背后有个旋转的图片 四.核心代码 html代码: <div class="bg3"> ...

  4. XCTF-open-source

    下载附件拿到源码. #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { if ( ...

  5. Lateral Movement

    简介 这次去宁夏护网,发现我有的朋友连最基本的横向渗透有些都不晓得,他们问我 我也表达不清楚...,就想着写篇文章总结下吧 (慢慢更..) 可以发我邮箱讨论:muxue@protonmail.com ...

  6. Centos配置网络和主机映射

    目录 虚拟机网络的三种配置方式 配置虚拟机IP 主机映射问题 配置虚拟机的主机名 虚拟机远程登录 虚拟机网络的三种配置方式 桥接模式:当前虚拟机与主机在同一个局域网下,同一个局域网下的所有电脑都可以访 ...

  7. 更换Swing界面中的窗口图标

    Swing 窗口图标更换 因为需要,所以要更改窗口的图标,很简单 在代码中加上 Image icon = Toolkit.getDefaultToolkit().getImage("图片地址 ...

  8. Java通过SSLEngine与NIO实现HTTPS访问

    Java使用NIO进行HTTPS协议访问的时候,离不开SSLContext和SSLEngine两个类.我们只需要在Connect操作.Connected操作.Read和Write操作中加入SSL相关的 ...

  9. Git(9)-- 远程仓库的使用

    @ 目录 1.查看远程仓库:git remote 2.添加远程仓库:git remote add 3.从远程仓库中抓取与拉取:git fetch和 git pull 4.推送到远程仓库:git pus ...

  10. Skywalking-07:OAL原理——解释器实现

    OAL 解释器实现 OAL 解释器是基于 Antlr4 实现的,我们先来了解下 Antlr4 Antlr4 基本介绍 Antlr4 使用案例 参考Antlr4的使用简介这篇文章,我们实现了一个简单的案 ...