文章目录

  • 4,AVL-平衡树
  • 5, 2-3-4 树
    • 5.1 概念介绍
    • 5.2 添加情况
    • 5.3 , 和红黑树的等价关系
      • 2节点
      • 3节点
      • 4节点
      • 超过4节点裂变的情况
      • 转换成红黑树
  • 6,红黑树
    • 定义红黑树类
    • 6.1, 旋转操作
      • 左旋
      • 右旋
      • 红黑树-旋转代码实现
        • 1,左旋代码实现
        • 2,右旋代码实现
    • 6.2, 插入操作
      • 1, 先完成插入节点
      • 2,接下来需要旋转和变色 调整红黑树的平衡
        • 2-3-4数插入节点情况
          • 2节点
          • 3节点
          • 4节点
      • 3.代码实现插入操作
        • 需要调整的共8种情况
        • 代码里面的例图
      • 4 put方法
      • 5测试put方法
        • 运行结果
        • 网站验证
    • 6.3, 删除操作
      • 6.3.1 二叉树删除
      • 6.3.2查找前驱后继节点
      • 6.3.3 . 红黑树删除代码实现
      • 6.3.4 删除后的调整
    • 6.4测试删除结果 (前驱)

4,AVL-平衡树

​ BST存在的问题是, 树在插入的时候会导致倾斜, 不同的插入顺序会导致数的高度不一样, 而树的高度直接影响了树的查找效率。

​ 最坏的情况所有的节点都在一条斜线上,这样树的高度为N。

​ 基于BST存在的问题, 平衡查找二叉树(Balanced BST)产生了。

​ 平衡树的插入和删除的时候, 会通过旋转操作将高度保持在LogN。

​ 其中两款具有代表性的平衡术分别为AVL树(高度平衡树, 具备二叉搜索树的全部特性, 而目左右子树高度差不超过1 )和红黑树。

AVL树是如何实现平衡的呢?,具体是通过左旋或者右旋来实现的。具体如下图:

填加8 9 10 时 左右高度超过1 右旋

添加6 , 7 时 第二次根据8 右旋

添加 5 第三次根据9 右旋

添加4 第四次根据6旋转

添加 2 ,3 ,结果 4不平衡了, 第五次根据4旋转

添加1 之后, 5不平衡了, 第六次根据5旋转

平衡树转成红黑树

  • 红黑数要求没有平衡数那么严格, 只要保证黑节点平衡: 每条道黑节点的数量相同

5, 2-3-4 树

5.1 概念介绍

2-3-4树是四阶的 B树(Balance Tree), 他属于一种多路查找树, 它的结构有以下限制:

所有叶子节点都拥有相同的深度。

节点只能是 2-节点、3-节点、4-节点之一。

  • 2-节点:包含1个元素的节点,有2个子节点:
  • 3-节点:包含2个元素的节点,有3个子节点:
  • 4-节点:包含3个元素的节点,有4个子节点:

所有节点必须至少包含1个元素

元素始终保持排序顺序,整体上保持二叉查找树的性质,即父结点大于左子结点,小于右子结点:

而且结点有多个元素时,每个元素必须大于它左边的和它的左子树中元素。

下图是一个典型的2-3-4树

  • 2节点 : 5
  • 3节点 : 7 9
  • 4节点 : 10 11 12

​ 2-3-4树的查间操作像普通的二叉搜索树一样,非常简单,但由于其结点元素数不确定,在一些编程语言中实现起来并不方便,实现一般使用它的等同------------红黑树。

5.2 添加情况

出现5节点时会发生裂变

发现: 都是从底层插入的, 超过4节点后裂变

5.3 , 和红黑树的等价关系

2节点

3节点

4节点

超过4节点裂变的情况

转换成红黑树

  • 看出根节点到每条路径的黑节点都是3

6,红黑树

红黑树, Red-Black Tree [RBT]是一个自平衡(不是绝对的平衡)的二叉查找树(BST), 树上的每个节点都遵循下面的规则:

  1. 每个节点要么是黑色,要么是红色。
  2. 根节点是黑色。
  3. 每个叶子节点 (NIL) 是黑色。
  4. 每个红色结点的两个子结点一定都是黑色。(不存在两个相邻的红色节点)
  5. 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

红黑树能自平衡, 它靠的是什么? 三种操作: 左旋、右旋和变色

操作 描述
左旋 以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点
右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变
右旋 以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,
左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
变色 结点的颜色由红变黑或由黑变红。

定义红黑树类

public class RBTree<K extends Comparable<K>, V> {//红色用false来表示private static final boolean RED = false;private static final boolean BLACK = true;//红黑树的root节点private RBNode root;public RBNode getRoot() {return root;}public void setRoot(RBNode root) {this.root = root;}//红黑树对象内部类//继承Comparable接口, 可以做比较static class RBNode<K extends Comparable<K>,V>{//父节点private RBNode parent;//左子节点private RBNode left;//右子节点private RBNode right;//颜色,  这里定义的是 黑--true  红--falseprivate boolean color;private  K key;private  V value;public RBNode() {}public RBNode(RBNode parent, RBNode left, RBNode right, boolean color, K key, V value) {this.parent = parent;this.left = left;this.right = right;this.color = color;this.key = key;this.value = value;}//为了操作简化, 再添加一个parent,key,valuepublic RBNode(RBNode parent, K key, V value) {this.parent = parent;this.key = key;this.value = value;}public RBNode getParent() {return parent;}public void setParent(RBNode parent) {this.parent = parent;}public RBNode getLeft() {return left;}public void setLeft(RBNode left) {this.left = left;}public RBNode getRight() {return right;}public void setRight(RBNode right) {this.right = right;}public boolean isColor() {return color;}public void setColor(boolean color) {this.color = color;}public K getKey() {return key;}public void setKey(K key) {this.key = key;}public V getValue() {return value;}public void setValue(V value) {this.value = value;}}
}

6.1, 旋转操作

左旋

以某个节点作为旋转点,其右子节点变为旋转节点的父节点,右子节点的左子节点变为旋转节点的右子节点,左子节点保持不变。

右旋

以某!个节点作为旋转点,其左子节点变为旋转节点的父节点,左子节点的右子节点变为旋转节点的左子节点,右子节点保持不变。

红黑树-旋转代码实现
1,左旋代码实现

左旋

只改变了两根线的指向,

  • rl父节点指向p, p, 右子节点指向rl
  • p的父节点指向pr, pr左子节点指向p
  /*** 实现左旋*      p                          pr*      /\                        /\*     pl  pr    ==>       p      rr*          /\              /\*         rl  rr         pl   rl*  左旋操作:*  不用变的:  p-pl , pr-rr*  需要调整:  1. pr-rl 调整为  p-rl ,*                    将rl调整为p的右子节点*                    将p调整为rl的父节点*                2.判断p是否有父节点*                有:  把pr.parent = p.parent*                     p为p.parent的子节点, 到底是 左还是右呢*                       if p.parent.left == p*                           p.parent.left = pr*                       else*                           p.parent.right = pr*                没有: 直接把pr设置成root节点*                3.最后  把p 和 pr交换*                p.parent = pr;  pr.left = p** @param p, 需要旋转的节点*///左旋方法private  void  leftRotate(RBNode p){if ( p != null ){//获取到pr节点RBNode pr = p.right;//获取到pr的左子节点 rlp.right =pr.left;
//            pr = rl;if ( pr.left != null ) {//将rl调整为p的右子节点
//                p.right = rl;//将p调整为rl的父节点pr.left.parent = p;}//2,判断p是否有父节点pr.parent = p.parent; //不管p是否有父节点, 都设置为pr的父节点if ( p.parent == null ){//如果p.parent 为空, 说明p为root节点, 把root改为prroot = pr ;//如果p是p.parent的左子节点, 就变更为p.parent.left  = pr}else  if ( p.parent.left == p ){p.parent.left = pr;}else {//如果p是p.parent的右子节点, 就变更为p.parent.right  = prp.parent.right = pr;}//3 最后 把p父节点设置为pr, pr的左子节点设置为ppr.left = p;p.parent = pr;}}
2,右旋代码实现

    /*** 右旋实现*      p                 pl*      /\                /\*    pl  pr    ==>    ll    p*    /\                    /\*   ll lr               lr   pr* 不变的 p-pr , pl-lr,  两个2节点的右字节点不变* 步骤1: p-pl 变成 p-lr* 步骤2: 判断p是否有父节点 , pl-lr 变成 pr-p* 步骤3:  p-pl 变成 pl-p* @param p*///右旋方法private  void  rightRotate(RBNode p){if (p != null) {RBNode pl = p.left;p.left = pl.right;//步骤1: p-pl 变成 p-lr , lr的父节点 = pif (pl.right != null) {//                p.left = lr;pl.right.parent = p;}//步骤2: 判断p是否有父节点,pl.parent = p.parent ;if (p.parent == null) {root = pl;}else if ( p.parent.left == p ){p.parent.left = pl;}else {p.parent.right = pl;}//步骤3:  p-pl 变成 pl-ppl.right = p;p.parent = pl;}}

6.2, 插入操作

1, 先完成插入节点
  • a.找到插入的位置(父节点)
  • b.将节点插入父节点相应的位置
//插入操作
private void put(K key,V value){//1, 找到插入的父节点RBNode t = root;if ( t == null ){ //这种情况说明是第一次插入root = new RBNode(null,key,value==null?key:value);return;}//记录比较情况int cmp;RBNode parent;do {parent = t;//比较当前根节点与传入的key大小来决定从哪边开始查找cmp = key.compareTo((K) t.key);if ( cmp > 0){ //从右侧查找t = t.right ;}else if ( cmp < 0 ){//从左侧查找t = t.left ;}else {// cmp =0 说明key相等了//如果value为空就设置为keyt.setValue( value == null ? key:value);return;}}while (t != null);//2, 将新节点添加到父节点的子节点中// a 创建要插入的节点RBNode node = new RBNode(parent, key, value == null ? key : value);if ( cmp > 0 ){//如果比父节点大, 就放到右边parent.right = node;}else {parent.left = node;}//旋转和变色 调整红黑树的平衡
}
2,接下来需要旋转和变色 调整红黑树的平衡
2-3-4数插入节点情况
2节点

如果是二节点插入1个节点, 只需判断放左右, 黑红数是平衡的

3节点

如果是3节点插入的话, 会出现6种情况

只有1, 6 不需要调整 , 3 ,4 旋转一次 变色就可以了

需要旋转两次的

图2先右旋再左旋

图5先左旋再右旋

4节点

会出现4种情况, 每种情况都需要调整

3.代码实现插入操作
需要调整的共8种情况

代码里面的例图

有右叔叔节点情况, 需变色(右叔叔跟父亲变为黑色, 爷爷变成红色), 这时平衡了

但是如果爷爷节点上面还有元素, 这时候就不平衡了;

这时的爷爷节点也是红色,就把爷爷节点当成新插入的元素再和上面再来一次变色处理(递归)

x = getParent(x)做完这步之后 x = 2了

现在发现, 图6 旋转一次后 就和 图5 一模一样了 , 可以一起处理了

4 put方法
package com.ccc.util.treemap;public class RBTree<K extends Comparable<K>, V> {//红色用false来表示private static final boolean RED = false;private static final boolean BLACK = true;//红黑树的root节点private RBNode root;public RBNode getRoot() {return root;}public void setRoot(RBNode root) {this.root = root;}//插入操作public void put(K key,V value){//1, 找到插入的父节点RBNode t = root;if ( t == null ){ //这种情况说明是第一次插入root = new RBNode(null,key,value==null?key:value);return;}//记录比较情况int cmp;RBNode parent;if (key == null){System.out.println("put 命令里 key 为空了");throw new NullPointerException();}do {parent = t;//比较当前根节点与传入的key大小来决定从哪边开始查找cmp = key.compareTo((K) t.key);if ( cmp > 0){ //从右侧查找t = t.right ;}else if ( cmp < 0 ){//从左侧查找t = t.left ;}else {// cmp =0 说明key相等了//如果value为空就设置为keyt.setValue( value == null ? key:value);return;}}while (t != null);//2, 将新节点添加到父节点的子节点中// a 创建要插入的节点RBNode node = new RBNode(parent, key, value == null ? key : value);if ( cmp > 0 ){//如果比父节点大, 就放到右边parent.right = node;}else {parent.left = node;}//旋转和变色 调整红黑树的平衡fixAfterPut(node);}//获取父节点private RBNode getParent(RBNode node){return node != null ? node.parent : null;}//获取爷节点private RBNode getGrandfather(RBNode node){return node != null ? node.parent.parent : null;}//获取左节点private RBNode getLeft(RBNode node){return node != null ? node.left : null;}//获取右节点private RBNode getRight(RBNode node){return node != null ? node.right : null;}//获取节点颜色private boolean getColor(RBNode node){//如果传过来的节点是空的, 就返回黑色, ----------------Treemap里删除的时候会用到这个表达式return node == null ? BLACK : node.color;}//设置颜色private void setColor(RBNode node, boolean color){if (node != null) {node.color = color;}}/*** 插入放点后的调整操作* 2-3-4对应的操作*     2书点: 新插入一个元素直接2节点合并不用调整*          红黑树:新增一个红色 放点在黑色节点下不需要调整*     3节点: 新插入一个元素在3节点下, 那么会出现6中情况(2两种不需要调整,4种需要调整)*         红黑树: 插入的节点是 插入在上黑下红的结构中,插入在红色节点*     4节点:新插入一个元素在4节点下,那么会出现4中情况都需要调整*         红黑树:新增的节点是红色, 爷爷节点是黑色,父亲节点和叔叔节点是红色* @param x*/private void  fixAfterPut(RBNode<K,Object> x){//插入的节点  肯定是红色x.color = RED;//2节点不用调整, 3, 4 节点才需要调整, 把2节点过滤了while ( x !=null && x != root && x.parent.color ==RED ){ //为空为root都不需要调整, 只有2节点的父节点是黑色,其他都是红色, 所以这里把2节点排除了//这里分为了两种情况,if ( getParent(x) == getLeft(getGrandfather(x))  ){  //x的父节点在x爷爷左子节点时  对应 图1 , 2 ,5, 6//需调整的变成了4种, 有叔叔节点: 图1,2     没有叔叔节点: 图5,6//找到右叔叔节点, 1,2找到4 , 5,6 找到nullRBNode uncleR = getRight(getGrandfather(x));//如果右叔叔的颜色是RED, 说明不为空 : 图 1,2if (getColor(uncleR) == RED) {//叔叔跟父亲变为黑色, 爷爷变成红色//但是还有一种情况是爷爷节点上面还有元素, 这下就把(下面这个整体)爷爷节点当成x ,再来一遍 如图9//变色+ 递归setColor(uncleR,BLACK); //把右叔叔设置成黑色setColor( getParent(x) , BLACK ); //把父节点设置成黑色setColor(getGrandfather(x),RED);//把爷爷设置成红色//递归处理,  就是先保证自身平衡, 再把这部分当做新元素和上面的节点变色处理x = getGrandfather(x);}else {//说明没有右叔叔节点: 图 5,6//判断x是父节点的左子节点还是右子节点if ( x == getRight(getParent(x))) { //是父节点的右子节点: 图6x = getParent(x);//现在x就转移到了2 , 如图10leftRotate(x); // 旋转之后, 下面5, 和10 可以一起处理了}//把父节点变为黑色, 爷爷节点变为红色, 再根据爷爷节点右旋即可setColor(getParent(x),BLACK);//父黑setColor(getGrandfather(x),RED);//爷红rightRotate(getGrandfather(x));//爷右旋}}else {//x的父节点在x爷爷右子节点时  对应 图3,4,7,8//需调整的变成了4种, 有叔叔节点: 图3,4     没有叔叔节点: 图7,8 , 和上面的情况左右相反//找到左叔叔节点, 图3,4找到2 , 7,8 找到nullRBNode uncleL = getLeft(getGrandfather(x));//如果左叔叔的颜色是RED, 说明不为空 : 图 3,4if (getColor(uncleL) == RED) {//叔叔跟父亲变为黑色, 爷爷变成红色//但是还有一种情况是爷爷节点上面还有元素, 这下就把(下面这个整体)爷爷节点当成x ,再来一遍 如图9//变色+ 递归setColor(uncleL,BLACK); //把左叔叔设置成黑色setColor( getParent(x) , BLACK ); //把父节点设置成黑色setColor(getGrandfather(x),RED);//把爷爷设置成红色//递归处理,  就是先保证自身平衡, 再把这部分当做新元素和上面的节点变色处理x = getGrandfather(x);}else {//说明没有左叔叔节点: 图 7,8//判断x是父节点的左子节点还是右子节点if ( x == getLeft(getParent(x))) { //是父节点的右子节点: 图7x = getParent(x);//现在x就转移到了3rightRotate(x); // 旋转之后, 下面7, 和8 可以一起处理了}//把父节点变为黑色, 爷爷节点变为红色, 再根据爷爷节点右旋即可setColor(getParent(x),BLACK);//父黑setColor(getGrandfather(x),RED);//爷红leftRotate(getGrandfather(x));//爷右旋}}}// root 节点肯定为黑色setColor(root , BLACK);}}
5测试put方法

运行结果

网站验证

Red/Black Tree Visualization (usfca.edu)

6.3, 删除操作

6.3.1 二叉树删除

二叉树删除操作的情况:

  1. 删除叶子节点,直接删除
  2. 删除的节点有一个子节点,那么用子节点来替代
  3. 如果删除的节点右两个子节点,此时需要找到前驱节点或者后继节点来替代,可以转换为 1、2的情况

删除节点方案:

  1. 找到前驱节点,复制前驱节点值覆盖预备删除的节点的值,然后删除前驱节点
  2. 找到后继节点,复制后继节点值覆盖预备删除的节点的值,然后删除后继节点

被删除的前驱或者后继节点只有两种情况

  1. 被删除的节点是叶子节点
  2. 被删除的节点有一个孩子节点
6.3.2查找前驱后继节点
    /*** 找到前驱节点* @param node* @return*/private RBNode getPrecursor(RBNode node){if (node != null  ) {return null;} else if (getLeft(node) != null) {//如果他的左节点不为空, 就循环找他的左节点的右子节点RBNode p = getLeft(node);while ( getRight(p) != null){p = getRight(p);}return  p;}else {//如果这个node没有左节点, 那么就向上找前驱节点//这种情况在 红黑树 ,2-3-4树中不会出现RBNode p = node.parent;RBNode n1 = node;while ( p!= null && n1 == getLeft(p)){n1 = p;p = getParent(p);}return p;}}/*** 找到后继节点* @param node* @return*/private RBNode getSuccessor(RBNode node){if (node != null  ) {return null;} else if (getRight(node) != null) {//如果他的左节点不为空, 就循环找他的左节点的右子节点RBNode p = getRight(node);while ( getLeft(p) != null){p = getLeft(p);}return  p;}else {//如果这个node没有左节点, 那么就向上找前驱节点//这种情况在 红黑树 ,2-3-4树中不会出现RBNode p = node.parent;RBNode n1 = node;while ( p!= null && n1 == getRight(p)){n1 = p;p = getParent(p);}return p;}}
6.3.3 . 红黑树删除代码实现
  • 删除叶子节点,直接删除
  • 删除的节点有一个子节点,那么用子节点来替代
  • 如果删除的节点右两个子节点,此时需要找到前驱节点或者后继节点来替代,可以转换为 1、2的情况
 /*** 删除节点*      1, 节点删除(可以看做普通的二叉树删除)*      2, 删除后调整* @param key* @return*/public V remove(K key){//1. 根据需要删除的key, 找到对应的node节点RBNode node = getNode(key);if ( node == null ){return  null;}V oldValue = (V) node.value;//具体删除节点的方法deleteNode(node);return oldValue;}/*** 根据key找到删除的对应node* @param key* @return*/private RBNode getNode(K key) {if ( key == null ){return null;}RBNode node = root;while ( node != null ){int cmp = node.key.compareTo(key);if (cmp > 0) {node = node.right ;} else if (cmp < 0) {node = node.left;}else {//找到了对应的节点return node;}}return null;}/*** 删除节点* 1.删除节点(普通的二叉树相同)*      a. 删除叶子节点, 直接删除*      b. 删除的节点有一个子节点, 用子节点来替代*      c. 删除的节点有两个子节点, 把这个节点赋值给前驱或后继节点, 再删除掉前去后继节点*      将情况c转换成情况a, b* 2.调整* @param node*/private void deleteNode(RBNode node) {//1, 先处理情况3if (getLeft(node) != null && getRight(node) != null) {//有两个子节点的情况RBNode pNode = getSuccessor(node);//找到后继或前驱节点,这里使用后继//用后继节点的值, 覆盖给删除节点node.key = pNode.key;node.value = pNode.value;//这时要删除的节点就变成了 后继节点, 也就变成了情况2 ,或1了node = pNode;}//找到后继节点的子节点(替代节点)RBNode replacement = node.left==null ? node.right:node.left;//2, 再情况2, 情况2 可能是情况3 转换来的if (replacement != null) { //如果后继节点有子节点说明是情况2replacement.parent = node.parent;if ( getParent(node) == null ){//说明我们删除的节点是根节点root = replacement;} else if (getLeft(getParent(node)) == node) { //如果是node父节点的左节点, 就换成replacementgetParent(node).left = replacement;}else { //如果是node父节点的右节点, 就换成replacementgetParent(node).right = replacement;}//要删除的node节点全部设置成空 , GCnode.right = node.parent = node.right = null ;if (getColor(node) == BLACK) { //如果删除的节点是红色, 不需要调整//做调整操作fixAfterRemove(replacement);}}else if (node.parent == null){ // node 没有父节点说明是root节点root = null;}else { //就是情况1//先调整再删除if (node.color == BLACK) {fixAfterRemove(node);}if (node.parent != null) { //如果他没有父节点再把指向关系去掉getParent(node).left = getParent(node).right  = null ;}node = null;}}private void fixAfterRemove(RBNode node) {}
6.3.4 删除后的调整

在整理红黑树节点的删除操作时我们需要先理解清楚红黑树删除和2-3-4树删除的等价关系,这样理解起 来才会比较容易

核心理论:红黑树删除操作的本质其实就是删除2-3-4树的叶子节点

情况一

情况2:删除的是非情况1的节点,根据我们前面介绍的删除的规则,会找到对应的前驱和后继节点,那 么最终删除的还是叶子节点

删除节点的调整操作:

1.情况一:自己能搞定的,对应叶子节点是3节点和4节点

2.情况二:自己搞不定,需要兄弟借,但是兄弟不借,找父亲借,父亲下来,然后兄弟找一个人去代替

父亲当家

这种情况就是兄弟节点是3节点或者4节点 找兄弟节点

如果找到的兄弟节点是红色其实还要调整

执行如下调整先,先变色,然后左旋

找兄弟节点借

然后沿着7节点左旋

3.情况三:跟兄弟借,兄弟也没有(情同手足,同时自损)

兄弟节点是2节点,同时当前节点的父节点是红色节点的情况

删除后直接变色就可以了 兄弟节点是2节点,同时当前节点的父节点是黑色节点

变更操作为如下,如果继续有父节点那么还要递归处理

最终的是实现代码为

    /*** 删除节点后的调整操作* 2-3-4数操作*  1. 删除3,4 节点, 自己能搞定*  2. 删除2 节点, 自己搞不定, 需要兄弟借, 兄弟借*      父亲下来, 兄弟找一个节点替换父亲节点位置*  3. 删除2 节点, 自己搞不定, 兄弟不借** @param node*/private void fixAfterRemove(RBNode node) {// 情况2 和3//如果是root节点直接改为黑色就可以了, 替换的节点是红色就不需要调整了while (node != root && getColor(node) == BLACK ){//判断node是父节点的左节点还是有节点if ( node == getLeft(getParent(node))){//1. 找到兄弟节点RBNode bro = getParent(node).right;// 判断找到的是不是真的兄弟节点 ---- 2-3-4 转红黑树3节点有两种情况if (getColor(bro) == RED) {//如果是红色, 找到的就不是真的兄弟节点, 需要一次变色加左旋转setColor(bro,BLACK);setColor(getParent(node),RED);leftRotate(getParent(node));bro = getParent(node).right;//找到真兄弟了}//判断能不能借//兄弟节点一个子节点都没有, 不借if (getColor(getLeft(bro)) == BLACK && getColor(getRight(bro)) == BLACK) {//2没有子节点, 不借setColor(bro,RED);node = getParent(node);}else {//2兄弟借//如果兄弟节点的子节点是左边, 需要先变色, 右旋一次if (getColor(getRight(bro)) == BLACK) {//右侧子节点为空, 那就有左子节点setColor(bro,RED);setColor(getLeft(bro),BLACK);rightRotate(bro);bro = getRight(getParent(node));}//需要根据父节点做一次左旋操作, 变色setColor(bro,getColor(getParent(node)));setColor(getParent(node),BLACK);setColor(getRight(bro),BLACK);leftRotate(getParent(node));node =root; //结束循环, 针对3的处理}}else {//1. 找到兄弟节点RBNode bro = getParent(node).left;// 判断找到的是不是真的兄弟节点 ---- 2-3-4 转红黑树3节点有两种情况if (getColor(bro) == RED) {//如果是红色, 找到的就不是真的兄弟节点, 需要一次变色加左旋转setColor(bro,BLACK);setColor(getParent(node),RED);rightRotate(getParent(node));bro = getParent(node).left;//找到真兄弟了}//判断能不能借//兄弟节点一个子节点都没有, 不借if (getColor(getLeft(bro)) == BLACK && getColor(getRight(bro)) == BLACK) {//2没有子节点, 不借setColor(bro,RED);node = getParent(node);}else {//2兄弟借//如果兄弟节点的子节点是左边, 需要先变色, 右旋一次if (getColor(getLeft(bro)) == BLACK) {//右侧子节点为空, 那就有左子节点setColor(bro,RED);setColor(getRight(bro),BLACK);leftRotate(bro);bro = getLeft(getParent(node));}//需要根据父节点做一次左旋操作, 变色setColor(bro,getColor(getParent(node)));setColor(getParent(node),BLACK);setColor(getLeft(bro),BLACK);rightRotate(getParent(node));node =root; //结束循环, 针对3的处理}}}//1. 替换的节点为红色, 只需要变色为黑色集客setColor(node,BLACK);}

6.4测试删除结果 (前驱)

初始状态一致

【数据结构笔记二】AVL-平衡树__2-3-4树__红黑树实现(b站波哥)相关推荐

  1. 数据结构与算法--Tree(二叉树、B±树、红黑树)

    在MySQL中,索引的的实现方式中使用的最多的就是B+Tree,那么为什么要选择B+Tree呢?我们就需要从最基本的二叉查找树说起 什么是二叉树? 二叉树 = (空树) | (节点+左右子二叉树) 解 ...

  2. 种树:二叉树、二叉搜索树、AVL树、红黑树、哈夫曼树、B树、树与森林

    虽然今天不是植树节,但是我今天想种树. 文章目录 树,什么是树? 二叉树 定义 二叉树的创建 二叉树的前中后序遍历 前序遍历: 中序遍历 后序遍历 已知前序.中序遍历结果,还原二叉树 已知后序.中序遍 ...

  3. AVL树和红黑树区别

    二叉查找树: 二叉查找树就是左结点小于根节点,右结点大于根节点的一种排序树,也叫二叉搜索树.也叫BST.二叉查找树比普通树查找更快,查找.插入.删除的时间复杂度为O(logN).但是二叉查找树有一种极 ...

  4. 二叉排序树、AVL树、红黑树、B树、B+树、Hash树、

    二叉排序树 1.基本应用 二叉排序树也称为也叫二叉查找树,二叉搜索树, BST. 满足二叉查找树的一般性质,是指一棵空树具有如下性质: 对于二叉树中的任何一个非叶子节点,要求左子节点比当前节点值小,右 ...

  5. 讲透学烂二叉树(五):分支平衡—AVL树与红黑树伸展树自平衡

    简叙二叉树 二叉树的最大优点的就是查找效率高,在二叉排序树中查找一个结点的平均时间复杂度是O(log₂N): 在<讲透学烂二叉树(二):树与二叉/搜索/平衡等树的概念与特征>提到 二叉排序 ...

  6. AVL树、splay树(伸展树)和红黑树比较

    AVL树.splay树(伸展树)和红黑树比较 一.AVL树: 优点:查找.插入和删除,最坏复杂度均为O(logN).实现操作简单 如过是随机插入或者删除,其理论上可以得到O(logN)的复杂度,但是实 ...

  7. B树,B+树,红黑树应用场景AVL树,红黑树,B树,B+树,Trie树

    B B+运用在file system database这类持续存储结构,同样能保持lon(n)的插入与查询,也需要额外的平衡调节.像mysql的数据库定义是可以指定B+ 索引还是hash索引. C++ ...

  8. 数据结构找你妹(一)从二叉树到红黑树的分析实现

    什么是查找? 好了,我们今天的主题是--找你妹.或许应该把话题提升到一个不那么"好听"的层次--查找.但还是从"好听"的讲起吧,我们应该都玩过"找你妹 ...

  9. 【题集】AVL树、伸展树、红黑树、二叉查找树、替罪羊树的时间复杂度

    目录 1. AVL树 2.伸展树 3.红黑树 4.二叉查找树 5.替罪羊树 1. AVL树 AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平 ...

  10. 【Python数据结构】——二叉平衡树AVL(查找、构建、删除、插入、打印、遍历)

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2021/7/28 20:57 # @Author : @linlianqin # @S ...

最新文章

  1. java是所有资源文件的索引_《Java程序设计》课程教学资源索引
  2. PAT甲级1077 Kuchiguse:[C++题解]字符串、最长公共后缀
  3. C语言实现中国象棋(Qt实现界面,源码下载,详细注释,易移植)
  4. 今日听闻这几款手机软件比较火爆 果然名不虚传!
  5. 【BZOJ - 2574】[Poi1999] Store-Keeper(点双连通分量,求割点,记忆化bfs)
  6. Ajax/JavaScript脚本大全,JS脚本大全
  7. 排难解惑 为您解开系统无法添加打印机之谜!
  8. sql命令(四)-操作数据表中的记录
  9. (通用版)salesforce中soql及sosl的伪‘Like’模糊检索
  10. Redfish协议测试工具–Postman
  11. sht11的linux程序,SHT11温湿度传感器的proteus仿真与程序源码
  12. 在线超市系统-PythonGUI Tkinter 图片界面设计案例
  13. Nginx:配置 try_files 实现内部重定向
  14. Severstal: Steel Defect Detection竞赛
  15. java源代码实现判断闰年和平年
  16. 用gauss消去法解线性方程组(数值数学实验教程P74ex5.2)-2021-11-03
  17. 基于React全家桶开发「网易云音乐PC」项目实战(三)
  18. 【camera】【摄像头模组】摄像头模组简单介绍
  19. 百问网物联网智能家居学习笔记day6
  20. 「科普」一文读懂生产制造MES系统

热门文章

  1. 【CSDN实训】面向对象编程的概念及应用方法
  2. 论文翻译——基于数据的最优直方图
  3. Vue嵌套路由 二级路由等详解
  4. JavaScript实现商品图片随鼠标动态全图放大
  5. ppt计算机说课教案,小学信息技术《制作图文并茂的幻灯片》说课稿
  6. DJYOS开源往事二:DJYOS开源工作室时期
  7. 开放原子训练营(第二季)RT-Thread Nano学习营线下学习心得
  8. 植物神经紊乱怎么办较好 别让无知害了你
  9. UG建模练习,大赛题目解析,ugnx1980
  10. 不同获取方式下TensorFlow(Keras)-CPU性能差异