AVL树

  • 二叉搜索树缺点分析
  • 改进二叉搜索树
    • 平衡(Balance)
    • 理想平衡
    • 如何改进二叉搜索树?
  • 平衡二叉搜索树(Balanced Binary Search Tree)
  • AVL树
    • BST 对比 AVLTree
    • 继承 BST
    • AVL树基础
    • 添加节点导致的失衡
      • LL – 右旋转(单旋)
      • RR – 左旋转(单旋)
      • LR – 先左旋,再右旋(双旋)
      • RL – 先右旋,再左旋(双旋)
      • 旋转之后维护的内容
      • 添加之后的修复图解
      • 添加之后的修复 - 代码实现
      • 统一所有的旋转操作
    • 删除节点导致的失衡
      • LL – 右旋转(单旋)
      • RR – 左旋转(单旋)
      • LR – RR左旋转,LL右旋转(双旋)
      • RL – LL右旋转,RR左旋转(双旋)
      • 删除之后的修复
    • AVL树总结
  • AVL树完整源码
  • 测试

数据结构与算法笔记目录:《恋上数据结构》 笔记目录

想加深 Java 基础推荐看这个: Java 强化笔记目录

AVL树是在 二叉搜索树 的基础上学习的。

二叉搜索树缺点分析

  • 当 n 比较大时,两者的性能差异比较大
  • 比如 n = 1000000 时,二叉搜索树的最低高度是 20,最高高度是 1000000;

由此可见,二叉搜索树添加节点时可能会导致二叉搜索树退化成链表;
删除节点时也可能会导致二叉搜索树退化成链表;

有没有办法防止二叉搜索树退化成链表?让添加、删除、搜索的复杂度维持在 O(logn)?

改进二叉搜索树

平衡(Balance)

平衡:当节点数量固定时,左右子树的高度越接近,这棵二叉树就越平衡(高度越低)

理想平衡

最理想的平衡,就是像完全二叉树、满二叉树那样,高度是最小的;

如何改进二叉搜索树?

首先,节点的添加、删除顺序是无法限制的,可以认为是随机的:

  • 所以,改进方案是:在节点的添加、删除操作之后,想办法让二叉搜索树恢复平衡(减小树的高度)

    如果接着继续调整节点的位置,完全可以达到理想平衡,但是付出的代价可能会比较大;
  • 比如调整的次数会比较多,反而增加了时间复杂度
  • 总结来说,比较合理的改进方案是:用尽量少的调整次数达到适度平衡即可
    一棵达到适度平衡的二叉搜索树,可以称之为:平衡二叉搜索树

平衡二叉搜索树(Balanced Binary Search Tree)

英文简称为:BBST

经典常见的平衡二叉搜索树有:

  • AVL树
    Windows NT 内核中广泛使用
  • 红黑树
    C++ STL(比如 map、set )
    Java 的 TreeMap、TreeSet、HashMap、HashSet
    Linux 的进程调度
    Ngix 的 timer 管理

一般也称它们为:自平衡的二叉搜索树(Self-balancing Binary Search Tree)

AVL树

AVL 树是最早发明的自平衡二叉搜索树之—

平衡因子(Balance Factor):某结点的左右子树的高度差

AVL树的特点:

  • 每个节点的平衡因子只可能是 1、0、-1
    (绝对值 ≤ 1,如果超过 1,称之为 “失衡")
  • 每个节点的左右子树高度差不超过1
  • 搜索、添加、删除的时间复杂度是 O(logn)O(logn)O(logn)

BST 对比 AVLTree

输入数据:35, 37, 34, 56, 25, 62, 57, 9, 74, 32, 94, 80, 75, 100, 16, 82

继承 BST

这里 AVLTree 要继承的 BST,与之前学习的 二叉搜索树 几乎一样,有点小区别;

  • 在添加节点之后增加了 afterAdd() 用于调整平衡;
  • 在删除节点之后增加了 afterRemove() 用于调整平衡;

注意:BST 要继承 BinaryTree;

import java.util.Comparator;/*** 二叉搜索树*/
@SuppressWarnings("unchecked")
public class BST<E> extends BinaryTree<E> {// 比较器,根据传入的比较器实现 compareTo() 方法private Comparator<E> comparator;public BST(Comparator<E> comparator) { // 可以传一个比较器this.comparator = comparator;}public BST() { // 不传比较器,相当于传入一个 nullthis(null); //}/*** 添加元素*/public void add(E element) {elementNotNullCheck(element); // 不能传入空节点// 传入第一个节点, 若根节点为null, 则该节点为根节点if (root == null) {root = createNode(element, null);size++;// 新添加节点之后的处理afterAdd(root);return;}// 添加的不是第一个节点, 找到父节点Node<E> parent = root;Node<E> node = root;int cmp = 0;do {// 比较【传入节点的元素值】与【父节点的元素值】cmp = compareTo(element, node.element); // 方向parent = node; // 父节点if (cmp > 0) { // 传入节点比父节点要大, 往右node = node.right;} else if (cmp < 0) { // 传入节点比父节点要小, 往左node = node.left;} else { // 相等,最好是覆盖掉, 也可以采取其他操作, 看具体需求node.element = element;return;}} while (node != null);// 上面只是找到了要添加位置的父节点, 下面要将元素添加进去Node<E> newNode = createNode(element, parent);if (cmp > 0) {parent.right = newNode;} else {parent.left = newNode;}size++;// 新添加节点之后的处理afterAdd(newNode);}/*** 根据传入的值删除元素*/public void remove(E element) {remove(node(element));}// 根据节点删除元素private void remove(Node<E> node) {if (node == null) return;size--;if (node.hasTwoChildren()) { // 度为2的节点// 找到后继节点Node<E> s = successor(node);// 用后继节点的值覆盖度为2的节点的值node.element = s.element;// 删除后继节点node = s;}// 删除node节点(node的度必然是1或者0)Node<E> replacement = node.left != null ? node.left : node.right;if (replacement != null) { // node是度为1的节点// 更改parentreplacement.parent = node.parent;// 更改parent的left、right的指向if (node.parent == null) { // node是度为1的节点并且是根节点root = replacement;} else if (node == node.parent.left) {node.parent.left = replacement;} else { // node == node.parent.rightnode.parent.right = replacement;}// 删除节点后的调整afterRemove(node);} else if (node.parent == null) { // node是叶子节点并且是根节点root = null;// 删除节点后的调整afterRemove(node);} else { // node是叶子节点,但不是根节点if (node == node.parent.left) {node.parent.left = null;} else { // node == node.parent.rightnode.parent.right = null;}// 删除节点后的调整afterRemove(node);}}/*** 添加node之后的调整*/protected void afterAdd(Node<E> node) {}/*** 删除node之后的调整*/protected void afterRemove(Node<E> node) {}// 根据元素值获取节点元素private Node<E> node(E element) {elementNotNullCheck(element);Node<E> node = root;while (node != null) {int cmp = compareTo(element, node.element);if (cmp < 0) {node = node.left;} else if (cmp > 0) {node = node.right;} else { // cmp == 0return node;}}return null;}// 节点元素比较private int compareTo(E e1, E e2) {if (comparator != null) { // 传入比较器则通过比较器比较return comparator.compare(e1, e2);}// 没传比较器,元素内部必须自行实现了 Comparable 接口return ((Comparable<E>) e1).compareTo(e2);}// 检测传入的节点是否为空private void elementNotNullCheck(E element) {if (element == null) { // 不能传入空节点throw new IllegalArgumentException("element must not null");}}}

AVL树基础

public class AVLTree<E> extends BST<E> {public AVLTree(Comparator<E> comparator) {super(comparator);}public AVLTree() {this(null);}// AVL树的节点,需要计算平衡因子,因此比普通二叉树多维护一个height属性(将height放入普通二叉树里没有用处,浪费空间)private static class AVLNode<E> extends Node<E> {int height = 1;public AVLNode(E element, Node<E> parent) {super(element, parent);}public int balanceFactor() { // 获取该节点平衡因子(左子树高度 - 右子树高度)int leftHeight = left == null ? 0 : ((AVLNode<E>) left).height;int rightHeight = right == null ? 0 : ((AVLNode<E>) right).height;return leftHeight - rightHeight;}public void updateHeight() { // 更新高度int leftHeight = left == null ? 0 : ((AVLNode<E>) left).height;int rightHeight = right == null ? 0 : ((AVLNode<E>) right).height;height = 1 + Math.max(leftHeight, rightHeight);}public Node<E> tallerChild() {int leftHeight = left == null ? 0 : ((AVLNode<E>) left).height;int rightHeight = right == null ? 0 : ((AVLNode<E>) right).height;if (leftHeight > rightHeight) return left;if (rightHeight > leftHeight) return right;// 高度一样则返回同方向的,左子节点则返回左,否则返回右return isLeftChild() ? left : right;}@Overridepublic String toString() {String parentString = "null";if (parent != null) {parentString = parent.element.toString();}return element + "_p(" + parentString + ")_h(" + height + ")";}}/*** 重写父类中的 createNode* 返回 AVLNode*/@Overrideprotected Node<E> createNode(E element, Node<E> parent) {return new AVLNode<>(element, parent);}/*** 判断传入节点是否平衡(平衡因子的绝对值 <= 1)*/private boolean isBalanced(Node<E> node) {return Math.abs(((AVLNode<E>) node).balanceFactor()) <= 1;}/*** 更新高度*/private void updateHeight(Node<E> node) {((AVLNode<E>) node).updateHeight();}
}

添加节点导致的失衡

示例:往下面这棵子树中添加 13

  • 最坏情况:可能会导致所有祖先节点都失衡
  • 父节点、非祖先节点,都不可能失衡

修复平衡的操作

  • LL – 右旋转(单旋)
  • RR – 左旋转(单旋)
  • LR – 先左旋,再右旋(双旋)
  • RL – 先右旋,再左旋(双旋)

有些教程里面:

  • 把右旋转叫做 zig,旋转之后的状态叫做 zigged
  • 把左旋转叫做 zag,旋转之后的状态叫做 zagged

LL – 右旋转(单旋)

p 成为这颗子树的根节点

  • g.left = p.right
  • p.right = g

旋转后仍然是一颗 二叉搜索树:T0 < n < T1 < p < T2 < g < T3

还需要注意维护的内容

  • T2pgparent 属性
  • 先后更新 gp 的高度
/*** 右旋转*/
private void rotateRight(Node<E> grand) {Node<E> parent = grand.left;Node<E> child = parent.right;grand.left = child;parent.right = grand;afterRotate(grand, parent, child);
}

RR – 左旋转(单旋)

p 成为这颗子树的根节点

  • g.right = p.left
  • p.left = g

旋转后仍然是一颗 二叉搜索树:T0 < g < T1 < o < T2 < n < T3

还需要注意维护的内容

  • T1pgparent 属性
  • 先后更新 gp 的高度
/*** 左旋转*/
private void rotateLeft(Node<E> grand) {Node<E> parent = grand.right;Node<E> child = parent.left;grand.right = child;parent.left = grand;afterRotate(grand, parent, child);
}

LR – 先左旋,再右旋(双旋)

LR 就是 先进行 左旋转 – RR、再进行 右旋转 – LL

  • 先左旋转:p.right = n.leftn.left = p
  • 再右旋转:g.left = n.rightn.right = g

旋转后仍然是一颗 二叉搜索树:T0 < p < T1 < n < T2 < g < T3

RL – 先右旋,再左旋(双旋)

RL 就是 先进行 右旋转 – LL、再进行 左旋转 – RR

  • 先右旋转:p.left = n.rightn.right = p
  • 再左旋转:g.right = n.leftn.left = g

旋转后仍然是一颗 二叉搜索树:T0 < g < T1 < n < T2 < p < T3

旋转之后维护的内容

/*** 公共代码:不管是左旋转、右旋转,都要执行的* @param grand 失衡节点* @param parent 失衡节点的tallerChild* @param child child g和p需要交换的子树(本来是p的子树, 后面会变成g的子树)*/
private void afterRotate(Node<E> grand, Node<E> parent, Node<E> child) {// 让parent成为子树的根节点parent.parent = grand.parent;if (grand.isLeftChild()) {grand.parent.left = parent;} else if (grand.isRightChild()) {grand.parent.right = parent;} else {// grand是root节点root = parent;}// 更新child的parentif (child != null) {child.parent = grand;}// 更新grand的parentgrand.parent = parent;// 更新高度updateHeight(grand);updateHeight(parent);
}

添加之后的修复图解

输入数据:13, 14, 15, 12, 11, 17, 16, 8, 9, 1

输入13:正常
输入14:正常
输入15:13失衡,RR,左旋转

输入12:正常
输入11:13失衡,LL,右旋转

输入17:正常
输入16:15失衡,RL,先右选择、再左旋转

输入8:正常
输入9:11失衡,LR,先左旋转、再右旋转

输入1:12失衡,LL,右旋转

添加之后的修复 - 代码实现

/*** 增加节点后的调整*/
@Override
protected void afterAdd(Node<E> node) {while ((node = node.parent) != null) {if (isBalanced(node)) { // 如果平衡// 更新高度updateHeight(node);} else { // 如果不平衡// 恢复平衡rebalance(node);// 只要恢复了最下面的子树的平衡, 则整棵树恢复平衡break;}}
}
/*** 恢复平衡* @param grand 高度最低的那个不平衡节点*/
private void rebalance(Node<E> grand) {Node<E> parent = ((AVLNode<E>) grand).tallerChild();Node<E> node = ((AVLNode<E>) parent).tallerChild();if (parent.isLeftChild()) {//Lif (node.isLeftChild()) {//LLrotateRight(grand);//LL则右旋} else {//LRrotateLeft(parent);rotateRight(grand);}} else {//Rif (node.isLeftChild()) {//RLrotateRight(parent);rotateLeft(grand);} else {//RRrotateLeft(grand);//RR则左旋}}
}

统一所有的旋转操作

/*** 统一旋转*/
private void rotate(Node<E> r, // 子树的根节点Node<E> b, Node<E> c,Node<E> d,Node<E> e, Node<E> f) {// 让d成为这颗子树的根结点d.parent = r.parent;if (r.isLeftChild()) {r.parent.left = d;} else if (r.isRightChild()) {r.parent.right = d;} else {root = d;}// b-cb.right = c;if (c != null) {c.parent = b;}updateHeight(b);// e-ff.left = e;if (e != null) {e.parent = f;}updateHeight(f);// b-d-fd.left = b;d.right = f;b.parent = d;f.parent = d;updateHeight(d);
}
private void rebalance(Node<E> grand) {Node<E> parent = ((AVLNode<E>) grand).tallerChild();Node<E> node = ((AVLNode<E>) parent).tallerChild();if (parent.isLeftChild()) {//Lif (node.isLeftChild()) {//LLrotate(grand, node, node.right, parent, parent.right, grand);} else {//LRrotate(grand, parent, node.left, node, node.right, grand);}} else {//Rif (node.isLeftChild()) {//RLrotate(grand, grand, node.left, node, node.right, parent);} else {//RRrotate(grand, grand, parent.left, parent, node.left, node);}}
}

删除节点导致的失衡

示例:删除子树中的 16

  • 可能会导致父节点祖先节点失衡(只有1个节点会失衡),其他节点,都不可能失衡

LL – 右旋转(单旋)

  • 如果绿色节点不存在,更高层的祖先节点可能也会失衡,需要再次恢复平衡,然后又可能导致更高层的祖先节点失衡…
  • 极端情况下,所有祖先节点都需要进行恢复平衡的操作,共 O(logn) 次调整

RR – 左旋转(单旋)

LR – RR左旋转,LL右旋转(双旋)

RL – LL右旋转,RR左旋转(双旋)

删除之后的修复

/*** 删除节点后的调整*/
@Override
protected void afterRemove(Node<E> node) {while ((node = node.parent) != null) {if (isBalanced(node)) {// 更新高度updateHeight(node);} else {// 恢复平衡rebalance(node);}}
}

AVL树总结

添加

  • 可能会导致所有祖先节点都失衡
  • 只要让高度最低的失衡节点恢复平衡,整棵树就恢复平衡【仅需 O(1) 次调整】

删除

  • 可能会导致父节点祖先节点失衡(只有1个节点会失衡)
  • 恢复平衡后,可能会导致更高层的祖先节点失衡【最多需要 O(logn) 次调整】

平均时间复杂度

  • 搜索:O(logn)
  • 添加:O(logn),仅需 O(1) 次的旋转操作
  • 删除:O(logn),最多需要 O(logn) 次的旋转操作

AVL树完整源码

package com.mj.tree;import java.util.Comparator;public class AVLTree<E> extends BST<E> {public AVLTree(Comparator<E> comparator) {super(comparator);}public AVLTree() {this(null);}// AVL树的节点,需要计算平衡因子,因此比普通二叉树多维护一个height属性(将height放入普通二叉树里没有用处,浪费空间)private static class AVLNode<E> extends Node<E> {int height = 1;public AVLNode(E element, Node<E> parent) {super(element, parent);}public int balanceFactor() { // 获取该节点平衡因子(左子树高度 - 右子树高度)int leftHeight = left == null ? 0 : ((AVLNode<E>) left).height;int rightHeight = right == null ? 0 : ((AVLNode<E>) right).height;return leftHeight - rightHeight;}public void updateHeight() { // 更新高度int leftHeight = left == null ? 0 : ((AVLNode<E>) left).height;int rightHeight = right == null ? 0 : ((AVLNode<E>) right).height;height = 1 + Math.max(leftHeight, rightHeight);}public Node<E> tallerChild() {int leftHeight = left == null ? 0 : ((AVLNode<E>) left).height;int rightHeight = right == null ? 0 : ((AVLNode<E>) right).height;if (leftHeight > rightHeight) return left;if (rightHeight > leftHeight) return right;// 高度一样则返回同方向的,左子节点则返回左,否则返回右return isLeftChild() ? left : right;}@Overridepublic String toString() {String parentString = "null";if (parent != null) {parentString = parent.element.toString();}return element + "_p(" + parentString + ")_h(" + height + ")";}}/*** 增加节点后的调整*/@Overrideprotected void afterAdd(Node<E> node) {while ((node = node.parent) != null) {if (isBalanced(node)) { // 如果平衡// 更新高度updateHeight(node);} else { // 如果不平衡// 恢复平衡rebalance(node);// AVL树中, 只要恢复了最下面的子树的平衡, 则整棵树恢复平衡break;}}}/*** 删除节点后的调整*/@Overrideprotected void afterRemove(Node<E> node) {while ((node = node.parent) != null) {if (isBalanced(node)) {// 更新高度updateHeight(node);} else {// 恢复平衡rebalance(node);}}}/*** 重写父类中的 createNode* 返回 AVLNode*/@Overrideprotected Node<E> createNode(E element, Node<E> parent) {return new AVLNode<>(element, parent);}/*** 判断传入节点是否平衡(平衡因子的绝对值 <= 1)*/private boolean isBalanced(Node<E> node) {return Math.abs(((AVLNode<E>) node).balanceFactor()) <= 1;}/*** 更新高度*/private void updateHeight(Node<E> node) {((AVLNode<E>) node).updateHeight();}/*** 恢复平衡* @param grand 高度最低的那个不平衡节点*/private void rebalance2(Node<E> grand) {Node<E> parent = ((AVLNode<E>) grand).tallerChild();Node<E> node = ((AVLNode<E>) parent).tallerChild();if (parent.isLeftChild()) { // Lif (node.isLeftChild()) { // LLrotateRight(grand); // LL则右旋} else { // LRrotateLeft(parent);rotateRight(grand);}} else { // Rif (node.isLeftChild()) { // RLrotateRight(parent);rotateLeft(grand);} else { // RRrotateLeft(grand); // RR则左旋}}}private void rebalance(Node<E> grand) {Node<E> parent = ((AVLNode<E>) grand).tallerChild();Node<E> node = ((AVLNode<E>) parent).tallerChild();if (parent.isLeftChild()) {//Lif (node.isLeftChild()) {//LLrotate(grand, node, node.right, parent, parent.right, grand);} else {//LRrotate(grand, parent, node.left, node, node.right, grand);}} else {//Rif (node.isLeftChild()) {//RLrotate(grand, grand, node.left, node, node.right, parent);} else {//RRrotate(grand, grand, parent.left, parent, node.left, node);}}}/*** 统一旋转*/private void rotate(Node<E> r, // 子树的根节点Node<E> b, Node<E> c,Node<E> d,Node<E> e, Node<E> f) {// 让d成为这颗子树的根结点d.parent = r.parent;if (r.isLeftChild()) {r.parent.left = d;} else if (r.isRightChild()) {r.parent.right = d;} else {root = d;}// b-cb.right = c;if (c != null) {c.parent = b;}updateHeight(b);// e-ff.left = e;if (e != null) {e.parent = f;}updateHeight(f);// b-d-fd.left = b;d.right = f;b.parent = d;f.parent = d;updateHeight(d);}/*private void rotate(Node<E> r, // 子树的根节点Node<E> a, Node<E> b, Node<E> c,Node<E> d,Node<E> e, Node<E> f, Node<E> g) {// 让d成为这颗子树的根结点d.parent = r.parent;if(r.isLeftChild()){r.parent.left = d;}else if(r.isRightChild()){r.parent.right = d;}else{root = d;}// a-b-cb.left = a;if(a!=null){a.parent = b;}b.right = c;if(c!=null){c.parent = b;}updateHeight(b);// e-f-gf.left = e;if(e != null){e.parent = f;}f.right = g;if(g != null){g.parent = f;}updateHeight(f);// b-d-fd.left = b;d.right = f;b.parent = d;f.parent = d;updateHeight(d);}*//*** 左旋转*/private void rotateLeft(Node<E> grand) {Node<E> parent = grand.right;Node<E> child = parent.left;grand.right = child;parent.left = grand;afterRotate(grand, parent, child);}/*** 右旋转*/private void rotateRight(Node<E> grand) {Node<E> parent = grand.left;Node<E> child = parent.right;grand.left = child;parent.right = grand;afterRotate(grand, parent, child);}/*** 公共代码:不管是左旋转、右旋转,都要执行的* @param grand 失衡节点* @param parent 失衡节点的tallerChild* @param child child g和p需要交换的子树(本来是p的子树, 后面会变成g的子树)*/private void afterRotate(Node<E> grand, Node<E> parent, Node<E> child) {// 让parent成为子树的根节点parent.parent = grand.parent;if (grand.isLeftChild()) {grand.parent.left = parent;} else if (grand.isRightChild()) {grand.parent.right = parent;} else {// grand是root节点root = parent;}// 更新child的parentif (child != null) {child.parent = grand;}// 更新grand的parentgrand.parent = parent;// 更新高度updateHeight(grand);updateHeight(parent);}}

测试

package com.mj;import com.mj.printer.BinaryTrees;
import com.mj.tree.AVLTree;public class Main {// Integer类型的数据public static void test1(){Integer date[] = new Integer[] {75, 94, 21, 7, 93, 31, 83, 65, 43, 50, 57, 56};AVLTree<Integer> avl = new AVLTree<>();for (int i = 0; i < date.length; i++) {avl.add(date[i]);System.out.println("【" + date[i] + "】");BinaryTrees.println(avl);System.out.println("-----------------------------------------");}}// 删除public static void test2(){Integer data[] = new Integer[] {35, 37, 34, 56, 25, 62, 57, 9, 74, 32, 94, 80, 75, 100, 16, 82};AVLTree<Integer> avl = new AVLTree<>();for (int i = 0; i < data.length; i++) {avl.add(data[i]);
//          System.out.println("【" + data[i] + "】");
//          BinaryTrees.println(avl);
//          System.out.println("---------------------------------------");}for (int i = 0; i < data.length; i++) {avl.remove(data[i]);System.out.println("【" + data[i] + "】");BinaryTrees.println(avl);System.out.println("---------------------------------------");}BinaryTrees.println(avl);}public static void main(String[] args) {// test1();test2();}
}
【35】┌──────────37_p(null)──────────┐│                              │┌─────25_p(37)─────┐         ┌────────62_p(37)────────┐│                  │         │                        │
9_p(25)─┐        ┌─34_p(25) 56_p(62)─┐         ┌──────80_p(62)─────┐│        │                   │         │                   │16_p(9) 32_p(34)             57_p(56) 74_p(80)─┐         ┌─94_p(80)─┐│         │          │75_p(74) 82_p(94)   100_p(94)
---------------------------------------
【37】┌───────────56_p(null)──────────┐│                               │┌─────25_p(56)─────┐           ┌───────80_p(56)──────┐│                  │           │                     │
9_p(25)─┐        ┌─34_p(25) ┌─62_p(80)─┐           ┌─94_p(80)─┐│        │          │          │           │          │16_p(9) 32_p(34)   57_p(62)    74_p(62)─┐ 82_p(94)   100_p(94)│75_p(74)
---------------------------------------
【34】┌─────────56_p(null)────────┐│                           │┌─25_p(56)─┐           ┌───────80_p(56)──────┐│          │           │                     │
9_p(25)─┐  32_p(25) ┌─62_p(80)─┐           ┌─94_p(80)─┐│           │          │           │          │16_p(9)    57_p(62)    74_p(62)─┐ 82_p(94)   100_p(94)│75_p(74)
---------------------------------------
【56】┌────────57_p(null)────────┐│                          │┌─25_p(57)─┐           ┌──────80_p(57)─────┐│          │           │                   │
9_p(25)─┐  32_p(25) ┌─74_p(80)─┐         ┌─94_p(80)─┐│           │          │         │          │16_p(9)    62_p(74)    75_p(74) 82_p(94)   100_p(94)
---------------------------------------
【25】┌────────57_p(null)────────┐│                          │┌─16_p(57)─┐           ┌──────80_p(57)─────┐│          │           │                   │
9_p(16)    32_p(16) ┌─74_p(80)─┐         ┌─94_p(80)─┐│          │         │          │62_p(74)    75_p(74) 82_p(94)   100_p(94)
---------------------------------------
【62】┌───────57_p(null)───────┐│                        │┌─16_p(57)─┐         ┌──────80_p(57)─────┐│          │         │                   │
9_p(16)    32_p(16) 74_p(80)─┐         ┌─94_p(80)─┐│         │          │75_p(74) 82_p(94)   100_p(94)
---------------------------------------
【57】┌─────74_p(null)────┐│                   │┌─16_p(74)─┐         ┌─80_p(74)─┐│          │         │          │
9_p(16)    32_p(16) 75_p(80)  ┌─94_p(80)─┐│          │82_p(94)   100_p(94)
---------------------------------------
【9】┌─────74_p(null)────┐│                   │
16_p(74)─┐         ┌─80_p(74)─┐│         │          │32_p(16) 75_p(80)  ┌─94_p(80)─┐│          │82_p(94)   100_p(94)
---------------------------------------
【74】┌─────75_p(null)────┐│                   │
16_p(75)─┐         ┌─94_p(75)─┐│         │          │32_p(16) 80_p(94)─┐ 100_p(94)│82_p(80)
---------------------------------------
【32】┌─80_p(null)─┐│            │┌─75_p(80)    ┌─94_p(80)─┐│             │          │
16_p(75)      82_p(94)   100_p(94)
---------------------------------------
【94】┌─80_p(null)─┐│            │┌─75_p(80)   ┌─100_p(80)│            │
16_p(75)     82_p(100)
---------------------------------------
【80】┌─82_p(null)─┐│            │┌─75_p(82)     100_p(82)│
16_p(75)
---------------------------------------
【75】┌─82_p(null)─┐│            │
16_p(82)     100_p(82)
---------------------------------------
【100】┌─82_p(null)│
16_p(82)
---------------------------------------
【16】
82_p(null)
---------------------------------------
【82】
---------------------------------------

《恋上数据结构第1季》平衡二叉搜索树、AVL树相关推荐

  1. 《恋上数据结构第1季》二叉搜索树BST

    二叉搜索树(BinarySearchTree) BST 接口设计 BST 基础 添加元素: add() 删除元素: remove() 删除节点 – 叶子节点 删除节点 – 度为1的节点 删除节点 – ...

  2. 《恋上数据结构第1季》二叉堆实现优先级队列

    优先级队列(Priority Queue) 优先级队列简介 优先队列的底层实现 二叉堆实现优先级队列源码 测试代码 数据结构与算法笔记目录:<恋上数据结构> 笔记目录 想加深 Java 基 ...

  3. 《恋上数据结构第1季》二叉堆原理及实现、最小堆解决 TOP K 问题

    二叉堆 BinaryHeap 堆(Heap) 堆的出现 堆简介 二叉堆(Binary Heap) 获取最大值 最大堆 - 添加 最大堆 - 添加优化 最大堆 - 删除 replace 最大堆 - 批量 ...

  4. 五.树,二叉树,二叉搜索树(BST)和自平衡二叉搜索树(AVL)

    1.树 树是一种数据结构 比如:目录结构 树是一种可以递归定义的数据结构 树是由n个节点组成的集合: 如果 n=0, 那这是一颗空树 如果 n>0, 那存在1个节点作为树的根节点,其他节点可以分 ...

  5. VC++2012编程演练数据结构《9》平衡二叉搜索树

    平衡二叉搜索树 任何结点的左子树和右子树高度最多相差1的二叉搜索树. (1)AVL树的插入算法 a. 插入结点之后仍然是AVL树,则不调整: b. 插入结点之后不再满足AVL树条件,则进行调整,根据导 ...

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

    简介 平衡二叉搜索树是一种特殊的二叉搜索树.为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜索树所有的节点都是右节点,那么这个二叉搜索树将会退化成为链表.从而导致搜索的时间复 ...

  7. C++ 第八节数据结构 第七节 ——二叉搜索树 AVL树 红黑树(底层原理图+模拟实现)

    第一次,C++和数据结构联合推出,倾情献上呦~~ 给个关注吧 23333~~~~~~(现在每天系统就给我一个机器人的粉丝量了55555~~~~~) 本节内容,我们将着重来探讨 二叉树 中特殊的两种树- ...

  8. 详解 二叉搜索树-----AVL树

    二叉搜索树 根结点比左子树中所有结点都大 根结点比右子树所有结点都小 最小的元素在最左侧 最大的元素在最右侧 中序遍历有序 具有以上的特征的二叉树就是二叉搜索树也叫二叉排序数 二叉搜索树的操作 查找 ...

  9. C++泛型编程实现平衡二叉搜索树AVL

    代码如下: #include <iostream> using namespace std;template <typename T> struct AVLNode {type ...

最新文章

  1. 【读书笔记】《高性能JavaScript》
  2. 交易平台基本密钥处理流程(SJL05加密机)
  3. 【Netty】使用 Netty 开发 HTTP 服务器
  4. viewGroup 项目中使用
  5. 数据分析_SQL数据分析--旅游数据分析可视化实操
  6. 【DP】【四边形不等式】邮局(P4767)
  7. Win11系统显示你的账户已被停用怎么办
  8. SQL Server 2008——SQL命令INSERT
  9. 谷歌聊天机器人api_如何编写针对Google地图等网络应用量身定制的聊天机器人
  10. redis原子性读写操作之LUA脚本和watch机制
  11. 详解SSH框架和Redis的整合
  12. 无所不能的『十五郎』向您致敬!!!
  13. python使用ip地址定位_python实现ip地址查询经纬度定位详解
  14. 2007年考研时间安排表
  15. 语音合成:transformer tts 论文复现以及dockerfile
  16. 用Python做一个猜数游戏(入门)
  17. python讲得比较好的老师_Python学习讲师哪个好?
  18. [BDSec CTF 2022] 部分WP
  19. 海贝播放器在iso上无法识别cue文件的问题
  20. 如何编写敏捷开发中的user story

热门文章

  1. 人不能活在舒适区里,我要趁年轻出去闯闯
  2. But don‘t be fooled, this phone does not
  3. linux中的 127.0.0.1和0.0.0.0和::
  4. Dubbo+Zookeeper 基础讲解
  5. Mysql Workbench详细使用教程
  6. C++STL之fill()函数使用方法
  7. C语言实现http的下载
  8. linux7新建用户,CentOS 7中添加一个新用户并授权
  9. elt和etl_ETL和ELT架构概述
  10. 小甲鱼Python第十九讲课后习题