首页 > 其他分享 >AVL树、2-3-4树、红黑树节点增加删除原理(详细说明)

AVL树、2-3-4树、红黑树节点增加删除原理(详细说明)

时间:2024-08-21 15:52:29浏览次数:8  
标签:parentOf parent color AVL right 红黑树 节点 left

AVL树与红黑树

  • 引入:BST(二叉查找树)在插入的时候会导致倾斜,不同的插入顺序会导致树的高度不一样,树的高度直接影响到树的查找效率,最坏的情况就是所有节点就在一条斜线上,导致树的高度为N。平衡二叉树(Balanced BST)在插入和删除的时候,会通过旋转将高度保持在Logn。

删除节点:

        BST树:

                删除的是叶子结点:直接删除该节点,不需要进一步调整

                删除的节点只有一个叶子结点:将该节点的子节点连接到该节点的父节点上,替换被删除的节点

                删除的节点有两个子节点:找到删除节点的前驱节点(即左子树中的最大节点)或后继节点(即右子树中的最小节点),然后用前驱或后继节点的值替换要删除节点的值,并删除前驱或后继节点。

        AVL树:

                和BST相同,AVL树在删除节点时也会寻找要删除节点的前驱或后继节点,进行替换并删除。

                自平衡调整:在删除操作完成后,AVL树会检查从删除节点位置向上回溯的路径,判断是否有节点因高度失衡(即左子树和右子树的高度差超过1)而需要进行旋转操作(如左旋、右旋、左-右旋或右-左旋)来重新平衡树。

  • AVL(高度平衡树)具备二叉搜索树的全部特性,左右子树高度差不超过1。
    • 具体实现:
    • 缺点:AVL树要求太过严格,左旋和右旋的开销会比较大。

左旋:将树中的某个节点(通常是高度失衡的节点)向左旋转

右旋:将树中的某个节点(通常是高度失衡的节点)向右旋转

  • 2-3-4树:是四阶的B树,属于一种多路查找树,结构有以下限制,所有叶子结点都拥有相同的深度(即从根节点到任意一个叶子节点的路径长度(也称为深度)是相同的)。节点只能是2节点、3节点、4节点之一
    • 2节点:包含1个元素的节点,有2个子节点;
    • 3节点:包含2个元素的节点,有3个子节点;
    • 4节点:包含3个元素的节点,有4个子节点;
  • 红黑树:黑节点平衡,本质为2-3-4树
    • 2节点:一个2节点对应的红黑树节点就是一个黑色节点
    • 3节点:一个3节点可以有两种情况的红黑树节点,一种是右倾,一种是左倾,所以一个2-3-4树可以有多个红黑树(上黑下红)
    • 4节点:一个4节点转换的情况只用一种,中间节点黑色,左右节点红色。
    • 裂变状态:在2-3-4树中存在裂变状态,转换为红黑树后会先变色(不能有两个相邻的红色节点)

       由于最多为四节点,也就是包含3个元素节点,所以中间节点会变为父节点且为黑色,左右子节点均为红色,为图2状态。

       可见12、13都为红色且相邻,违背了红黑树规则,所以13作为12的右节点为红色,12则变为黑色,这样会导致从根节点到达叶子结点的路径中黑节点数量不一致,所以10变为黑色,11变为红色。如果11为根节点,则保持黑色

  • 2-3-4树转为红黑树:遵循上诉对应节点变色          
  • 红黑树:是一个自平衡(不是绝对的平衡)的二叉查找树,遵循以下规则:
    • 每个节点要么是黑色,要么是红色
    • 根节点是黑色
    • 每个叶子结点(NIL)是黑色。(在每一个节点中,包含叶子结点,该节点中的子节点不满足一个左节点一个右节点,则会使用虚拟节点nil进行填充,nil虚拟节点为黑色节点)
    • 每个红色节点的两个子节点都是黑色
    • 任意一节点到每个叶子结点的路径都包含相同的黑节点
    • 通过左旋、右旋、变色来保持平衡(左旋和右旋为二叉树通用)
    • 插入节点:
      • ​​​​​​​可见,(2)中两种情况类似将第二层进行分别右旋、左旋之后
      • 进行右旋、左旋之后与图(1)中的1以及图(3)中的2相似,之后进行变色处理,将第二层变为黑色,第一层变为红色,也就是如图这种形式
      • 这种形式左子树含有一个黑色节点,右子树没有,显然不符合红黑树特征,可以通过右旋来达到平衡效果。
    • ​​​​​​​​​​​​​​删除节点
      • 情况一:删除的是对应2-3-4树中的3节点或4节点,则可以直接删除,内部可以进行调整保持平衡
      •  情况二:节点内部无法调整保持平衡,需要找兄弟节点借(2-3-4树中的3节点或4节点),以删除图中6为例,在2-3-4树中兄弟节点为8;但是在对应的红黑树中,6的兄弟节点为9且为红色(称为假兄弟节点,需要进行调整)
      • 情况三:节点内部无法调整保持平衡,兄弟节点也没有可借的(2-3-4树中兄弟节点是2节点)父节点为红色
      • 情况四:节点内部无法调整保持平衡,兄弟节点也没有可借的(2-3-4树中兄弟节点是2节点)父节点为黑色,需要对上面的父节点继续递归处理

红黑树插入节点代码实现

class Node {
    int data;
    Node left, right, parent;
    boolean color; // true表示红色,false表示黑色

    public Node(int data) {
        this.data = data;
        this.left = null;
        this.right = null;
        this.parent = null;
        this.color = true; // 新节点默认是红色
    }
}

public class RedBlackTree {
    private Node root;
    private Node TNULL; // 表示NIL节点

    // 构造函数
    public RedBlackTree() {
        TNULL = new Node(0);
        TNULL.color = false; // NIL节点是黑色
        root = TNULL;
    }

    // 左旋操作
    private void leftRotate(Node x) {
        Node y = x.right;
        x.right = y.left;
        if (y.left != TNULL) {
            y.left.parent = x;
        }
        y.parent = x.parent;
        if (x.parent == null) {
            root = y;
        } else if (x == x.parent.left) {
            x.parent.left = y;
        } else {
            x.parent.right = y;
        }
        y.left = x;
        x.parent = y;
    }

    // 右旋操作
    private void rightRotate(Node y) {
        Node x = y.left;
        y.left = x.right;
        if (x.right != TNULL) {
            x.right.parent = y;
        }
        x.parent = y.parent;
        if (y.parent == null) {
            root = x;
        } else if (y == y.parent.right) {
            y.parent.right = x;
        } else {
            y.parent.left = x;
        }
        x.right = y;
        y.parent = x;
    }

    // 插入节点并修复红黑树
    public void insert(int key) {
        Node node = new Node(key);
        node.parent = null;
        node.data = key;
        node.left = TNULL;
        node.right = TNULL;
        node.color = true; // 新节点默认为红色

        Node y = null;
        Node x = root;

        while (x != TNULL) {
            y = x;
            if (node.data < x.data) {
                x = x.left;
            } else {
                x = x.right;
            }
        }

        node.parent = y;
        if (y == null) {
            root = node;
        } else if (node.data < y.data) {
            y.left = node;
        } else {
            y.right = node;
        }

        if (node.parent == null) {
            node.color = false;
            return;
        }

        if (node.parent.parent == null) {
            return;
        }

        fixInsert(node);
    }

    // 修复红黑树
    private void fixInsert(Node k) {
        Node u;
        while (k.parent.color == true) {
            if (k.parent == k.parent.parent.right) {
                u = k.parent.parent.left; // 叔叔节点
                if (u.color == true) {
                    // Case 1: 叔叔节点是红色
                    u.color = false;
                    k.parent.color = false;
                    k.parent.parent.color = true;
                    k = k.parent.parent;
                } else {
                    if (k == k.parent.left) {
                        // Case 2: k 是左子节点
                        k = k.parent;
                        rightRotate(k);
                    }
                    // Case 3: k 是右子节点
                    k.parent.color = false;
                    k.parent.parent.color = true;
                    leftRotate(k.parent.parent);
                }
            } else {
                u = k.parent.parent.right; // 叔叔节点

                if (u.color == true) {
                    // Case 1: 叔叔节点是红色
                    u.color = false;
                    k.parent.color = false;
                    k.parent.parent.color = true;
                    k = k.parent.parent;
                } else {
                    if (k == k.parent.right) {
                        // Case 2: k 是右子节点
                        k = k.parent;
                        leftRotate(k);
                    }
                    // Case 3: k 是左子节点
                    k.parent.color = false;
                    k.parent.parent.color = true;
                    rightRotate(k.parent.parent);
                }
            }
            if (k == root) {
                break;
            }
        }
        root.color = false; // 根节点必须是黑色
    }

    // 辅助函数:中序遍历打印树
    public void printTree() {
        printHelper(this.root, "", true);
    }

    private void printHelper(Node root, String indent, boolean last) {
        if (root != TNULL) {
            System.out.print(indent);
            if (last) {
                System.out.print("R----");
                indent += "   ";
            } else {
                System.out.print("L----");
                indent += "|  ";
            }

            String sColor = root.color ? "RED" : "BLACK";
            System.out.println(root.data + "(" + sColor + ")");
            printHelper(root.left, indent, false);
            printHelper(root.right, indent, true);
        }
    }

    public static void main(String[] args) {
        RedBlackTree tree = new RedBlackTree();

        // 插入节点
        tree.insert(10);
        tree.insert(20);
        tree.insert(30);
        tree.insert(40);
        tree.insert(50);
        tree.insert(25);

        // 打印树
        tree.printTree();
    }
}

TreeMap节点插入源码分析

private V put(K key, V value, boolean replaceOld) {
        Entry<K,V> t = root;
        if (t == null) {
            addEntryToEmptyMap(key, value);
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        } else {
            Objects.requireNonNull(key);
            @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }
        addEntry(key, value, parent, cmp < 0);
        return null;
    }
private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (addToLeft)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
    }
private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }

红黑树为二叉树,数据量大就会导致深度较深

标签:parentOf,parent,color,AVL,right,红黑树,节点,left
From: https://blog.csdn.net/weixin_52096141/article/details/141394546

相关文章

  • 电力系统潮流计算(牛顿-拉夫逊法、高斯-赛德尔法、快速解耦法)【6节点 9节点 14节点 26
      ......
  • 平衡二叉树、B树、B+树、红黑树解析
    目录有序二叉树平衡二叉树构造平衡二叉树RRLLRLLR平衡二叉树的优缺点:2-3-4树红黑树B树B+树B树、B+树、红黑树的应用有序二叉树关于有序二叉树的详解以及Ja......
  • 零壹塔(力扣,递归,找父节点)
    https://leetcode.cn/problems/k-th-symbol-in-grammar/0/\01/\/0110/\/\/\/\01101001#include<iostream>usingnamespacestd;intsolve(i......
  • 数据结构之 红黑树入门教程、红黑树代码示例
    红黑树(Red-BlackTree)是一种自平衡的二叉查找树(BST),它在插入、删除和查找操作后通过一些特定的规则来维护树的平衡,从而确保这些操作的时间复杂度始终为O(logn)。红黑树主要应用在需要高效动态集合操作的场景中,如操作系统中的进程调度器、数据库中的索引等。红黑树的基本性......
  • 基于STM32(STM32F103RETX)项目:水质检测与水位控制器(节点板)
    目录项目介绍一、项目需求二、设计方案三、相关技术点四、预计效果设备开发一、TDS模块二、LORA模块项目介绍一、项目需求1.水资源保护与管理的需求随着工业化和城市化的快速发展,水资源的污染问题日益严重,对水质进行实时监测和管理变得尤为重要。水质检测与水......
  • RabbitMQ消息队列:概念、单节点和集群示例
    目录消息队列概念主流的消息队列消息队列名词(1)Broker(2)Topic(3)Producer(4)Consumer(5)Queue(6)Message消息队列中两种工作模式Point-to-Point(PTP、点到点)Pub/Sub消息队列的缺点系统可用性降低系统复杂性提高数据一致性无法保证RabbitMQ相关术语(1)生产者(2)消费者(3)队......
  • 红黑树
    红黑树1=》2=》3不平衡二叉树-左旋1=》2=》3不平衡二叉树-右旋3=》2=》1平衡二叉树-变色10=》8=》20=》4=》9=》15=》28=》20......
  • 在K8S中,⼀个pod的不同container能够分开被调动到不同的节点上吗?
    在Kubernetes(K8S)中,一个Pod是一组一起部署和管理的容器的集合。Pod内的容器总是被调度到同一个节点上运行,这是因为Pod设计的基本理念是其内的所有容器需要紧密耦合并且共享相同的网络命名空间和存储卷。具体来说,Pod内的容器有以下特点:共享IP地址:Pod内的所有容器共享......
  • Elsa V3学习之分支节点
    接下来我们来介绍下Elsa的一些内置节点的使用。本节介绍分支节点。Descision这个节点其实就是If,只不过是用flow编排的模式。我们来创建一个简单的分支流程,通过HTTP节点请求的参数,判断是否满足表达式,分别输出True,False。添加一个变量,将HTTPEndpoint的OUTPUT的QueryStringData......
  • Elsa V3学习之循环节点
    上篇我们学习了分支节点,这篇文章我们来学习循环节点。Forfor节点跟我们代码中的for循环是一样的效果,有三个参数。Start,End,Step。分别表示起始数字,终点数字,以及步长,即每次循环加几的意思。下面的配置相当于for(i=0,i<=10,i++)。for节点的output表示当前的循环的值,我们可以......