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

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;
    }

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

  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值