zoukankan      html  css  js  c++  java
  • 手撸红黑树

    正文

    红黑树也是二叉查找树,我们知道,二叉查找树这一数据结构并不难,而红黑树之所以难是难在它是自平衡的二叉查找树,在进行插入和删除等可能会破坏树的平衡的操作时,需要重新自处理达到平衡状态。

    红黑树定义和性质

    红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质:

    • 性质1:每个节点要么是黑色,要么是红色。
    • 性质2:根节点是黑色。
    • 性质3:每个叶子节点(NIL)是黑色。
    • 性质4:每个红色结点的两个子结点一定都是黑色。
    • 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点(又叫黑高)。

    节点的结构

    static class Node<K extends Comparable<K>, V> {
            private Node<K, V> parent;
            private Node<K, V> left;
            private Node<K, V> right;
            private boolean red;
            private K key;
            private V value;
    
            public Node(K key, V value, boolean red) {
                this.key = key;
                this.value = value;
                this.red = red;
            }
    
        }
    

    插入

    插入的情景

    1 红黑树为空树

    把插入结点作为根结点,并把结点设置为黑色。

    2 插入结点的Key已存在

    更新当前结点的值为插入结点的值

    3 插入结点的父结点为黑结点

    直接插入

    4 插入结点的父结点为红结点

    4.1 叔叔结点存在并且为红结点

    父节点染成黑色
    叔叔节点染成黑色
    爷爷节点染成红色
    将爷爷节点设置成当前节点,继续修复

    4.2 叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点

    4.2.1 插入结点是其父结点的左子结点(ll双红)

    父节点染成黑色
    爷爷节点染成红色
    对爷爷节点右旋

    4.2.2 插入结点是其父结点的右子结点(lr双红)

    对父节点进行左旋
    将父节点当作当前节点进行4.2.1处理

    4.3 叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点

    4.3.1 插入结点是其父结点的右子结点(rr双红)

    将父亲节点染成黑色
    爷爷节点染成红色
    对爷爷节点左旋

    4.3.2 插入结点是其父结点的左子结点(rl双红)

    对父节点进行右旋
    将父节点当作当前节点进行4.3.2处理

     public void put(K key, V value) {
            if (root == null) {
                //根节点必须是黑的
                root = new Node<>(key, value, false);
            }
            Node<K, V> node = root, parent = null;
            int i = 0;
    	//找出待插入的节点
            while (node != null) {
                i = key.compareTo(node.key);
                //key等于时
                if (i == 0) {
                    break;
                } else if (i < 0) {
                    parent = node;
                    node = node.left;
                } else {
                    parent = node;
                    node = node.right;
                }
            }
            node = new Node<>(key, value, true);
            node.parent = parent;
            if (i < 0) {
                parent.left = node;
            } else if (i > 0) {
                parent.right = node;
            }
    	//修复红黑树
            check(node);
        }
    

    红黑树修复

    private void check(Node<K, V> node) {
            //p是父节点  pp爷爷节点  u叔叔节点
            Node<K, V> p, pp, u;
    
            //将根节点染成黑色
            root.red = false;
            p = node.parent;
            //父节点是黑色,插入的节点是红色,不影响树结构,直接插入
            //父节点是红色
            if (p!=null){
                //父节点是红色
                if (p.red){
                    pp = p.parent;
                    //判断叔叔节点在哪边
                    u = p.equals(pp.left) ? pp.right : pp.left;
                    //叔叔节点不存在或者叔叔节点是黑色
                    if (u == null || !u.red) {
                        if (p.equals(pp.left)) {
                            if (node.equals(p.left)) {
                                //对应ll 双红结构
                                ll(node);
                            } else {
                                //对应lr 双红结构
                                lr(node);
                            }
                        } else {
                            if (node.equals(p.right)) {
                                //对应rr 双红结构
                                rr(node);
                            } else {
                                //对应rl 双红结构
                                rl(node);
                            }
                        }
                    } else {
                        //叔叔节点存在   将叔叔和父亲节点染成黑色   爷爷节点染成红色  以爷爷节点进行下一步修复
                        p.red = false;
                        u.red = false;
                        pp.red = true;
                        check(pp);
                    }
                }
            }
    
        }
    
      // ll双红  p->黑  pp-> 红 将父节点变成黑色  爷爷节点变成红色  然后右旋
        private void ll(Node<K, V> node) {
            Node<K, V> p = node.parent, pp = p.parent, t;
            p.red = false;
            pp.red = true;
            //右旋
            t = p.right;
            p.right = pp;
            pp.left = t;
    
            p.parent = pp.parent;
            if (pp.parent != null) {
                if (pp.parent.left.equals(pp)) {
                    pp.parent.left = p;
                } else {
                    pp.parent.right = p;
                }
            } else {
                root = p;
            }
            pp.parent = p;
        }
    
        //lr结构   需要将 p和node左旋成 ll结构
        private void lr(Node<K, V> node) {
            Node<K, V> p = node.parent, pp = p.parent, t;
            //左旋成ll
            t = node.left;
            node.left = p;
            p.right = t;
    
            node.parent = p.parent;
            p.parent = node;
    
            ll(p);
        }
    
        //rr双红  p->黑  pp-> 红 将父节点变成黑色  爷爷节点变成红色  然后左旋
        private void rr(Node<K, V> node) {
            Node<K, V> p = node.parent, pp = p.parent, t;
            p.red = false;
            pp.red = true;
    
            //左旋
            t = p.left;
            p.left = pp;
            pp.right = t;
    
            p.parent = pp.parent;
            if (pp.parent != null) {
                if (pp.parent.left.equals(pp)) {
                    pp.parent.left = p;
                } else {
                    pp.parent.right = p;
                }
            } else {
                root = p;
            }
            pp.parent = p;
    
    
        }
    
    
        //rl结构   需要将 p和node右旋成 rr结构
        private void rl(Node<K, V> node) {
            Node<K, V> p = node.parent, pp = p.parent, t;
            //右旋
            t = node.right;
            node.right = p;
            p.left = t;
    
            node.parent = p.parent;
            p.parent = node;
    
            rr(p);
    
        }
    
  • 相关阅读:
    虎虎的小尾巴:期货长线换月损失太大怎么处理?
    虎虎的小尾巴:做期货怎么看基本面? 2020-02-27
    C++开发--在Visual Studio2013中使用boost::split()编译过程中出现error C4996
    C++开发--在Visual Studio2013中使用boost库
    Android开发--ZZ:Android APK反编译详解(附图)
    [Python]小百合十大爬虫
    Android开发-- findViewById()方法得到空指针
    Android开发-- The content of the adapter has changed but ListView did not receive a notification
    Android开发-- 简单对话框
    Python学习--判断变量的数据类型
  • 原文地址:https://www.cnblogs.com/huisunan/p/14738523.html
Copyright © 2011-2022 走看看