1. 程式人生 > 程式設計 >原來手寫紅黑樹可以這麼簡單

原來手寫紅黑樹可以這麼簡單

紅黑樹由4階B樹演化而來,瞭解AVL樹的左旋、右旋和4階B樹的上溢、下溢對我們學習紅黑樹有著事半功倍的效果

多路平衡搜尋樹——B樹

  1. 每個節點,所有子樹高度相同

  2. m階B樹表示節點的子節點個數最多為m,2-3樹就是3階B樹,2-3-4樹就是4階B樹,以此類推

  3. 節點的元素個數限制:

    • 根節點:1 <= x <= m - 1
    • 非根節點:(⌈m/2⌉ - 1) <= x <= (m - 1);
  4. 節點的子節點個數限制:

    • 2 <= x <= m
    • ⌈m/2⌉ <= x <= m
  5. 新增元素:新新增的元素必定會新增到葉子節點中

  6. 上溢:當向某個節點新增元素導致該節點元素個數超出限制

    。此時需將該節點中間元素(從左到右第 m>>1 個)沿父指標向上移動,並將左右兩邊的元素作為該元素的左右子節點

    Snipaste_2019-11-26_16-11-06.png

    這是唯一一種能讓B樹長高的情況,即新增元素時,上溢傳播到了根節點

  7. 刪除元素

    1. 當刪除葉子節點中的元素時,直接移除即可
    2. 當刪除非葉子節點中的元素時,先將其前驅/後繼元素覆蓋該元素,然後再刪除其前驅/後繼元素
    3. 某元素的前驅或後繼元素必定在葉子節點中
    4. 真正的刪除必定發生在葉子節點中
  8. 下溢:

    1. 下溢:刪除某元素後,該元素所在節點元素個數變為( ⌊m/2⌋ -2)

    2. 如果下溢節點有元素個數為(⌊m/2⌋)或以上的同層相鄰節點,可從該節點“借”一個元素

      1. 將下溢節點A和滿足條件的相鄰節點B中間的父節點元素複製一份新增到A中

      2. 將B中的最大元素(如果B在A的左邊)或最小元素(如果B在A的右邊)覆蓋父節點元素

      3. 刪除B中的最大/最小元素

        Snipaste_2019-11-26_15-34-02.png
    3. 如果下溢節點的同層相鄰節點元素小於或等於(⌊m/2⌋-1),這時沒辦法借了,則合併下溢節點和任意一個相鄰節點,並將兩者之間的父節點元素也扯下來

      Snipaste_2019-11-26_15-38-51.png

      1. 合併之後的節點元素個數為( ⌊m/2⌋ + ⌊m/2⌋ -2),不超過(m-1)
      2. 此操作可能會導致父節點下溢,下溢現象可能沿父節點向上傳播

    如果下溢調整時,扯下一個父節點元素之後導致父節點沒有元素了,此時樹的高度減一

  9. 依序向4階B樹(2-3-4樹)中新增1~22這22個元素步驟圖:

    10-52-49.png

    10-58-15.png

    10-58-43.png

  10. 依序刪除22~1

    11-41-02.png

    11-41-10.png

    11-41-17.png

紅黑樹

紅黑樹的性質

11-52-26.png

上圖中的樹並不是一棵紅黑色,因為綠色路徑上只有兩個黑色節點,而其他紅色路徑有3個黑色節點,不符合性質5

紅黑樹 VS 2-3-4樹

12-39-46.png

當B樹葉子節點元素分別為:紅黑紅、黑紅、紅黑、黑。(將紅黑樹當做2-3-4樹來看,只有黑色節點才能獨立成為一個B樹節點,且其左右紅色子節點可以融入該B樹節點中

14-30-03.png

新增元素

染成紅色新增到葉子節點中

14-30-24.png

新元素的定位邏輯與BST一致,需要注意的是新增之後的調整邏輯。

分情況討論

14-30-37.png

1、新增第一個元素

將節點染成黑色

2、新增的節點作為黑色節點的子節點

染成紅色新增即可,無需調整

15-05-50.png

3、新增的節點作為紅色節點的子節點,但無需上溢

LL/RR,單旋

14-30-57.png

RL/LR,雙旋

14-31-07.png

4、新增的節點作為紅色的子節點,且需上溢

LL上溢

14-45-03.png

RR上溢

14-45-18.png

LR上溢

14-45-28.png

RL上溢

14-45-54.png

程式碼實現

github: github.com/zanwen/algo…

二叉搜尋樹
package top.zhenganwen.learn.algorithm.datastructure.tree;

import top.zhenganwen.learn.algorithm.commons.printer.BinaryTreeInfo;
import top.zhenganwen.learn.algorithm.commons.printer.BinaryTrees;

import java.util.*;

import static java.util.Objects.isNull;

/**
 * @author zhenganwen
 * @date 2019/11/6 17:48
 */
public class BinarySearchTree<E> implements BinaryTreeInfo {

    protected Node<E>       root;

    private int           size;

    protected Comparator<E> comparator;

    public BinarySearchTree() {

    }

    public BinarySearchTree(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    public void add(E element) {
        nonNullCheck(element);

        if (root == null) {
            root = createNode(element,null);
            size++;
            afterAdd(root);
            return;
        }

        Node<E> parent = root,cur = root;
        int compare = 0;
        while (cur != null) {
            parent = cur;
            compare = compare(element,cur.element);
            cur = compare > 0 ? cur.right : compare < 0 ? cur.left : cur;
            if (cur == parent) {
                cur.element = element;
                return;
            }
        }
        Node<E> node = createNode(element,parent);
        if (compare > 0) {
            parent.right = node;
        } else {
            parent.left = node;
        }
        size++;
        afterAdd(node);
    }

    protected void afterAdd(Node<E> node) {

    }

    protected Node<E> createNode(E element,Node<E> parent) {
        return new Node<>(element,parent);
    }

    public void remove(E element) {
        remove(node(element));
    }

    private void remove(Node<E> node) {
        if (node == null)
            return;

        size--;
        if (hasTwoChild(node)) {
            // the node's degree is 2,use node's predecessor/successor's element
            // cover the node,and then delete the predecessor/successor
            Node<E> successor = Objects.requireNonNull(successor(node));
            node.element = successor.element;
            node = successor;
        }

        // reach here,the degree of the node is possible only 0 or 1
        // that is to say,the node only has one child
        Node replacement = node.left == null ? node.right : node.left;
        if (replacement != null) {
            // the node's degree is 1
            replacement.parent = node.parent;
            if (isRoot(node)) {
                root = replacement;
            } else if (compare(node.element,node.parent.element) >= 0) {
                node.parent.right = replacement;
            } else {
                node.parent.left = replacement;
            }
        } else {
            // the node is leaf node
            if (isRoot(node)) {
                root = null;
            } else if (compare(node.element,node.parent.element) >= 0) {
                node.parent.right = null;
            } else {
                node.parent.left = null;
            }
        }
        afterRemove(node);
    }

    protected void afterRemove(Node<E> node) {
        // let auto-balance bst overwrite the method to balance the tree
    }

    private boolean isRoot(Node<E> node) {
        return node.parent == null;
    }

    public boolean contains(E element) {
        return node(element) != null;
    }

    public void clear() {
        root = null;
        size = 0;
    }

    public Node node(E element) {
        Node<E> node = root;
        while (node != null) {
            int compare = compare(element,node.element);
            if (compare == 0)
                return node;
            else if (compare > 0) {
                node = node.right;
            } else {
                node = node.left;
            }
        }
        return null;
    }

    private Node predecessor(Node<E> node) {
        if (node.left != null) {
            node = node.left;
            while (node.right != null) {
                node = node.right;
            }
            return node;
        } else {
            Node parent = node.parent;
            while (parent != null) {
                if (node == parent.right) {
                    return parent;
                } else {
                    node = parent;
                    parent = node.parent;
                }
            }
            return null;
        }
    }

    private Node successor(Node<E> node) {
        if (node.right != null) {
            node = node.right;
            while (node.left != null) {
                node = node.left;
            }
            return node;
        } else {
            Node parent = node.parent;
            while (parent != null) {
                if (node == parent.left) {
                    return parent;
                } else {
                    node = parent;
                    parent = node.parent;
                }
            }
            return null;
        }
    }

    private int compare(E insert,E current) {
        if (comparator != null) {
            return Objects.compare(insert,current,comparator);
        }
        return ((Comparable<E>) insert).compareTo(current);
    }

    private void nonNullCheck(E element) {
        if (isNull(element)) {
            throw new IllegalArgumentException("element must not be null");
        }
    }

    @Override
    public Object root() {
        return root;
    }

    @Override
    public Object left(Object node) {
        return ((Node<E>) node).left;
    }

    @Override
    public Object right(Object node) {
        return ((Node<E>) node).right;
    }

    @Override
    public Object string(Object node) {
        return node;
    }

    protected static class Node<E> {
        E       element;
        Node<E> left;
        Node<E> right;
        Node<E> parent;

        Node(E element,Node<E> parent) {
            this(element);
            this.parent = parent;
        }

        Node(E element) {
            this.element = element;
        }

        boolean isLeftChildOf(Node node) {
            return this == node.left;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "element=" + element +
                    '}';
        }
    }

    private static boolean oneIsChildOfAnother(Node one,Node another) {
        return one != null && (one == another.left || one == another.right);
    }

    private static boolean isLeaf(Node node) {
        return node.left == null && node.right == null;
    }

}
複製程式碼
自平衡的二叉搜尋樹
package top.zhenganwen.learn.algorithm.datastructure.tree;

import java.util.Comparator;

/**
 * @author zhenganwen
 * @date 2019/11/28/028 15:53
 */
public class BBST<E> extends BinarySearchTree<E> {

    public BBST() {

    }

    public BBST(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    protected void rotateLeft(Node<E> node) {
        Node<E> child = node.right;
        // rotate left
        node.right = child.left;
        child.left = node;
        afterRotate(node,child);
    }

    protected void afterRotate(Node<E> node,Node<E> child) {
        // link parent
        child.parent = node.parent;
        if (node.parent == null)
            root = child;
        else if (node.isLeftChildOf(node.parent))
            node.parent.left = child;
        else
            node.parent.right = child;
        node.parent = child;
        if (node == child.right && node.left != null)
            node.left.parent = node;
        if (node == child.left && node.right != null)
            node.right.parent = node;
    }

    protected void rotateRight(Node<E> node) {
        Node<E> child = node.left;
        // rotate right
        node.left = child.right;
        child.right = node;
        afterRotate(node,child);
    }

    /**
     *
     * LL
     *
     * inorder traversal: a b c d e f g
     *                     |
     *              _______f______
     *             |             |
     *         ____d____         g                  ____d____
     *        |        |              ===>         |        |
     *       _b_       e                          _b_      _f_
     *      |  |                                 |  |     |  |
     *      a  c                                 a  c     e  g
     *
     *
     * RR
     *
     * inorder traversal: a b c d e f g
     *            |
     *        ____b____
     *       |        |
     *       a    ____d____                        ____d____
     *           |        |          ===>         |        |
     *           c       _f_                     _b_      _f_
     *                  |  |                    |  |     |  |
     *                  e  g                    a  c     e  g
     *
     * LR
     *
     * inorder traversal: a b c d e f g
     *                  |
     *            ______f_____
     *           |           |
     *        ___b___        g                  ____d____
     *       |      |             ===>         |        |
     *       a     _d_                        _b_      _f_
     *            |  |                       |  |     |  |
     *            c  e                       a  c     e  g
     *
     *
     * RL
     *
     * inorder traversal: a b c d e f g
     *             |
     *       ______b______
     *      |            |
     *      a         ___f___                  ____d____
     *               |      |    ===>         |        |
     *              _d_     g                _b_      _f_
     *             |  |                     |  |     |  |
     *             c  e                     a  c     e  g
     *
     *
     * @param r the root node of the child tree
     * @param a
     * @param b
     * @param c
     * @param d
     * @param e
     * @param f
     * @param g
     */
    protected 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 -> new root of the child tree
        d.parent = r.parent;
        if (r.parent == null)
            root = d;
        else if (r.isLeftChildOf(r.parent))
            r.parent.left = d;
        else
            r.parent.right = d;

        // a-b-c
        b.left = a;
        b.right = c;
        if (a != null)
            a.parent = b;
        if (c != null)
            c.parent = b;


        // e-f-g
        f.left = e;
        f.right = g;
        if (e != null)
            e.parent = f;
        if (g != null)
            g.parent = f;


        // b-d-f
        d.left = b;
        d.right = f;
        b.parent = d;
        f.parent = d;
    }
}
複製程式碼
紅黑樹
package top.zhenganwen.learn.algorithm.datastructure.tree;

import top.zhenganwen.learn.algorithm.commons.printer.BinaryTrees;

import java.util.Comparator;

/**
 * @author zhenganwen
 * @date 2019/11/28/028 15:52
 */
public class RBTree<E> extends BBST<E> {

    private static boolean RED   = false;
    private static boolean BLACK = true;

    public RBTree() {

    }

    public RBTree(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    @Override
    protected void afterAdd(Node<E> node) {
        // the insert node is root node or node overflows to top
        if (node.parent == null) {
            darken(node);
            return;
        }
        // the node is leaf node
        RBNode<E> parent = (RBNode<E>) node.parent;
        // 1. black parent
        if (parent.color == BLACK) {
            // redden it -> default red color
            return;
        }
        // 2. red parent,and grand must exist
        RBNode<E> uncle = sibling(parent);
        RBNode<E> grand = (RBNode<E>) parent.parent;
        if (isRed(uncle)) {
            // 2.1 overflow
            darken(parent);
            darken(uncle);
            redden(grand);
            afterAdd(grand);
            return;
        }
        // 2.2 uncle is null or black
        if (parent.isLeftChildOf(grand)) {
            if (node.isLeftChildOf(parent)) {
                // LL
                darken(parent);
                redden(grand);
                rotateRight(grand);
            } else {
                // LR
                darken(node);
                redden(grand);
                rotateLeft(parent);
                rotateRight(grand);
            }
        } else {
            if (node.isLeftChildOf(parent)) {
                // RL
                darken(node);
                redden(grand);
                rotateRight(parent);
                rotateLeft(grand);
            } else {
                // RR
                redden(grand);
                darken(parent);
                rotateLeft(grand);
            }
        }
    }

    private RBNode<E> color(Node<E> node,boolean color) {
        RBNode<E> n = (RBNode<E>) node;
        n.color = color;
        return n;
    }

    private RBNode redden(Node<E> node) {
        return color(node,RED);
    }

    private RBNode darken(Node<E> node) {
        return color(node,BLACK);
    }

    private boolean isRed(Node<E> node) {
        return node != null && ((RBNode<E>) node).color == RED;
    }

    private RBNode<E> sibling(Node<E> node) {
        if (node.parent == null) {
            return null;
        }
        if (node.isLeftChildOf(node.parent)) {
            return (RBNode<E>) node.parent.right;
        } else {
            return (RBNode<E>) node.parent.left;
        }
    }

    @Override
    protected Node<E> createNode(E element,Node<E> parent) {
        return new RBNode<>(element,parent);
    }

    private class RBNode<E> extends Node<E> {
        boolean color = RED;

        RBNode(E element,Node<E> parent) {
            super(element,parent);
        }

        @Override
        public String toString() {
            String s = "";
            if (color == RED) {
                s += "R_";
            }
            return s + element + "(" + (parent == null ? "null" : parent.element) + ")";
        }
    }

    public static void main(String[] args) {
        Integer[] arr = new Integer[]{
                89,90,40,21,81,58,79,85,98,12,15,91,96,69,18,66,47,43,82
        };
        RBTree<Integer> rbt = new RBTree<>();
        for (Integer i : arr) {
            System.out.println("【" + i + "】");
            rbt.add(i);
            BinaryTrees.println(rbt);
            System.out.println("=================================================");
        }
    }
}
複製程式碼

刪除元素

前提

14-46-19.png

1、刪除紅色節點

11-17-32.png

2、刪除黑色節點

11-17-39.png

2.1、刪除有一個紅色孩子的黑色節點

11-17-45.png

2.2、刪除沒有紅色孩子的黑色節點

2.2.1、被刪除節點的兄弟節點為黑色

11-17-52.png


11-18-37.png

2.2.2、被刪除節點的兄弟節點為紅色

11-18-43.png

程式碼實現

BST
package top.zhenganwen.learn.algorithm.datastructure.tree;

import top.zhenganwen.learn.algorithm.commons.printer.BinaryTreeInfo;
import top.zhenganwen.learn.algorithm.commons.printer.BinaryTrees;

import java.util.*;

import static java.util.Objects.isNull;

/**
 * @author zhenganwen
 * @date 2019/11/6 17:48
 */
public class BinarySearchTree<E> implements BinaryTreeInfo {

    protected Node<E>       root;

    private int           size;

    protected Comparator<E> comparator;

    public BinarySearchTree() {

    }

    public BinarySearchTree(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    public void add(E element) {
        nonNullCheck(element);

        if (root == null) {
            root = createNode(element,the degree of the node is only possible 0 or 1
        // that is to say,the node has no more than one child
        Node replacement = node.left == null ? node.right : node.left;
        if (replacement != null) {
            // the node's degree is 1
            replacement.parent = node.parent;
            if (isRoot(node)) {
                root = replacement;
            } else if (compare(node.element,node.parent.element) >= 0) {
                node.parent.right = replacement;
            } else {
                node.parent.left = replacement;
            }
            afterRemove(node,replacement);
        } else {
            // the node is leaf node
            if (isRoot(node)) {
                root = null;
            } else if (compare(node.element,node.parent.element) >= 0) {
                node.parent.right = null;
            } else {
                node.parent.left = null;
            }
            afterRemove(node,null);
        }
    }

    protected void afterRemove(Node<E> node,Node<E> replacement) {
        // let auto-balance bst overwrite the method to rebalance the tree
    }

    private boolean isRoot(Node<E> node) {
        return node.parent == null;
    }

    public boolean contains(E element) {
        return node(element) != null;
    }

    public void clear() {
        root = null;
        size = 0;
    }

    public Node node(E element) {
        Node<E> node = root;
        while (node != null) {
            int compare = compare(element,Node<E> parent) {
            this(element);
            this.parent = parent;
        }

        Node(E element) {
            this.element = element;
        }

        boolean isLeftChildOf(Node node) {
            return this == node.left;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "element=" + element +
                    '}';
        }
    }

    public static void preOrderUnRecur(Node root) {
        emptyTreeCheck(root);
        Stack<Node> stack = new Stack<>();
        StringBuilder stringBuilder = new StringBuilder();
        stack.push(root);
        while (!stack.isEmpty()) {
            Node node = stack.pop();
            stringBuilder.append(node.element).append(" ");
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        System.out.println(stringBuilder.toString());
    }

    private static void emptyTreeCheck(Node root) {
        if (root == null) {
            throw new IllegalArgumentException("empty tree");
        }
    }

    public static void inOrderUnRecur(Node root) {
        emptyTreeCheck(root);
        StringBuilder sb = new StringBuilder();
        Stack<Node> stack = new Stack<>();
        while (root != null) {
            stack.push(root);
            root = root.left;
            while (root == null) {
                if (stack.isEmpty()) {
                    break;
                } else {
                    Node node = stack.pop();
                    sb.append(node.element).append(" ");
                    root = node.right;
                }
            }
        }
        System.out.println(sb.toString());
    }

    private static void postOrderUnRecur(Node root) {
        emptyTreeCheck(root);
        StringBuilder stringBuilder = new StringBuilder();
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        Node lastAccess = null;
        while (!stack.isEmpty()) {
            Node node = stack.peek();
            // 當來到的節點node是葉子節點或上次訪問的節點是其子節點時,需要進行訪問
            if (isLeaf(node) || oneIsChildOfAnother(lastAccess,node)) {
                stack.pop();
                stringBuilder.append(node.element).append(" ");
                lastAccess = node;
            } else {
                if (node.right != null) {
                    stack.push(node.right);
                }
                if (node.left != null) {
                    stack.push(node.left);
                }
            }
        }
        System.out.println(stringBuilder.toString());
    }

    public static void levelOrder(Node root) {
        emptyTreeCheck(root);
        StringBuilder stringBuilder = new StringBuilder();
        LinkedList<Node> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            stringBuilder.append(node.element).append(" ");
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
        System.out.println(stringBuilder.toString());
    }

    private static boolean oneIsChildOfAnother(Node one,Node another) {
        return one != null && (one == another.left || one == another.right);
    }

    private static boolean isLeaf(Node node) {
        return node.left == null && node.right == null;
    }

    public static int height(Node root) {
        if (root == null) {
            return 0;
        }
        return Math.max(height(root.left),height(root.right)) + 1;
    }

    public static int heightUnRecur(Node root) {
        if (root == null) {
            return 0;
        }
        Stack<Node> s1 = new Stack<>(),s2 = new Stack<>();
        int height = 0;
        s1.push(root);
        while (!s1.isEmpty()) {
            while (!s1.isEmpty()) {
                Node node = s1.pop();
                if (node.left != null) {
                    s2.push(node.left);
                }
                if (node.right != null) {
                    s2.push(node.right);
                }
            }
            height++;
            Stack tmp = s1;
            s1 = s2;
            s2 = tmp;
        }
        return height;
    }

    public static boolean isCompleteBinaryTreeUnRecur(Node root) {
        if (root == null) {
            return true;
        }
        boolean leafMode = false;
        LinkedList<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            Node node = queue.pollFirst();
            if (leafMode) {
                if (!isLeaf(node)) {
                    return false;
                }
                continue;
            }
            if (hasTwoChild(node)) {
                queue.addLast(node.left);
                queue.addLast(node.right);
            } else if (node.left == null && node.right != null) {
                return false;
            } else {
                leafMode = true;
                if (node.left != null) {
                    queue.addLast(node.left);
                }
            }
        }
        return true;
    }

    private static boolean hasTwoChild(Node node) {
        return node != null && node.left != null && node.right != null;
    }

    public static void main(String[] args) {
        int[] arr = { 7,4,9,2,5,8,11,3,1 };
        BinarySearchTree<Integer> bst = new BinarySearchTree<>(Integer::compareTo);
        for (int i : arr) {
            bst.add(i);
        }
        BinaryTrees.println(bst);

        // remove node that degree is 0
//        bst.remove(1);
//        bst.remove(3);
//        bst.remove(12);
//        BinaryTrees.println(bst);

        // remove node that degree is 1
//        bst.remove(11);
//        BinaryTrees.println(bst);

        // remove node that degree is 2
//        bst.remove(4);
//        bst.remove(9);
//        BinaryTrees.println(bst);

        // remove root
        bst.remove(7);
        BinaryTrees.println(bst);


//        Node root = new Node(1);
//        root.left = new Node(2);
//        root.right = new Node(3);
//        root.left.left = new Node(4);
//        root.left.right = new Node(5);
//        root.right.left = new Node(6);
//        root.right.right = new Node(7);
//        root.left.left.left = new Node(8);
//        root.left.right.left = new Node(9);
//        root.left.right.left.left = new Node(10);

//        preOrderUnRecur(root);
//        inOrderUnRecur(root);
//        postOrderUnRecur(root);
//        System.out.println(height(root));
//        System.out.println(heightUnRecur(root));
//        System.out.println(isCompleteBinaryTreeUnRecur(root));
//        levelOrder(root);

    }

}
複製程式碼
BBST
package top.zhenganwen.learn.algorithm.datastructure.tree;

import java.util.Comparator;

/**
 * @author zhenganwen
 * @date 2019/11/28/028 15:53
 */
public class BBST<E> extends BinarySearchTree<E> {

    public BBST() {

    }

    public BBST(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    protected void rotateLeft(Node<E> node) {
        Node<E> child = node.right;
        // rotate left
        node.right = child.left;
        child.left = node;
        afterRotate(node,Node<E> g
    ) {
        // d -> new root of the child tree
        d.parent = r.parent;
        if (r.parent == null)
            root = d;
        else if (r.isLeftChildOf(r.parent))
            r.parent.left = d;
        else
            r.parent.right = d;

        // a-b-c
        b.left = a;
        b.right = c;
        if (a != null)
            a.parent = b;
        if (c != null)
            c.parent = b;


        // e-f-g
        f.left = e;
        f.right = g;
        if (e != null)
            e.parent = f;
        if (g != null)
            g.parent = f;


        // b-d-f
        d.left = b;
        d.right = f;
        b.parent = d;
        f.parent = d;
    }
}
複製程式碼
AVLTree
package top.zhenganwen.learn.algorithm.datastructure.tree;

import top.zhenganwen.learn.algorithm.commons.printer.BinaryTrees;

import java.util.Comparator;

/**
 * @author zhenganwen
 * @date 2019/11/25 13:46
 */
public class AVLTree<E> extends BBST<E> {

    public AVLTree() {

    }

    public AVLTree(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    /**
     * just need once rebalance
     *
     * @param node
     */
    @Override
    protected void afterAdd(Node<E> node) {
        while ((node = node.parent) != null) {
            if (isBalanced(node)) {
                updateHeight(node);
            } else {
                rebalance(node);
                break;
            }
        }
    }

    /**
     * remove the {@code node},maybe cause the LL or RR situation generating,* this depends on the height of right child's left height when remove left child's node
     * and the height of left child's right height when remove right child's node.
     * what's more,this time rebalance maybe cause the ancestor's unbalance.
     * <p>
     * maybe need O(logn) times rebalance. see red-black tree {@link RBTree}
     *
     * @param node
     */
    @Override
    protected void afterRemove(Node<E> node,Node<E> replacement) {
        while ((node = node.parent) != null) {
            if (isBalanced(node)) {
                updateHeight(node);
            } else {
                rebalance(node);
            }
        }
    }

    /**
     * see {@link this#rebalance)}
     * 平衡方案一:左旋右旋分開來做
     *
     * @param node
     */
    private void rebalance2(Node<E> node) {
        AVLNode grand = (AVLNode) node;
        AVLNode parent = getTallerChild(grand);
        AVLNode child = getTallerChild(parent);
        if (parent == grand.left) {
            if (child == parent.left) {
                // LL rotate right
                rotateRight(grand);
            } else {
                // LR rotate left first and then rotate right
                rotateLeft(parent);
                rotateRight(grand);
            }
        } else {
            if (child == parent.right) {
                // RR rotate left
                rotateLeft(grand);
            } else {
                // RL rotate right first and then rotate left
                rotateRight(parent);
                rotateLeft(grand);
            }
        }
    }

    /**
     * see {@link this#rebalance2}
     * 平衡方案二:從四種變換中抽離出通用的邏輯
     *
     * @param node
     */
    private void rebalance(Node<E> node) {
        AVLNode grand = (AVLNode) node;
        AVLNode parent = getTallerChild(grand);
        AVLNode child = getTallerChild(parent);
        if (parent == grand.left) {
            if (child == parent.left) {
                /*
                  LL
                           _______f______
                          |             |
                      ____d____         g
                     |        |
                    _b_       e
                   |  |
                   a  c

                   f -> grand,d -> parent,b -> child
                 */
                rotate(grand,cast(child.left),child,cast(child.right),parent,cast(parent.right),grand,cast(grand.right));
            } else {
                /*
                  LR
                       ______f_____
                      |           |
                   ___b___        g
                  |      |
                  a     _d_
                       |  |
                       c  e

                  f -> grand,b -> parent,d -> child
                 */
                rotate(grand,cast(parent.left),cast(grand.right));
            }
        } else {
            if (child == parent.right) {
                /*
                  RR
                   ____b____
                  |        |
                  a    ____d____
                      |        |
                      c       _f_
                             |  |
                             e  g

                  b -> grand,f -> child
                 */
                rotate(grand,cast(grand.left),cast(child.right));

            } else {
                /*
                  RL
                   ______b______
                  |            |
                  a         ___f___
                           |      |
                          _d_     g
                         |  |
                         c  e

                  b -> grand,f -> parent,cast(parent.right));
            }
        }
    }

    @Override
    protected void afterRotate(Node<E> node,Node<E> child) {
        super.afterRotate(node,child);
        ((AVLNode) node).updateHeight();
        ((AVLNode) child).updateHeight();
    }

    @Override
    protected void rotate(Node<E> r,Node<E> g) {
        super.rotate(r,a,b,c,d,e,f,g);
        ((AVLNode) b).updateHeight();
        ((AVLNode) f).updateHeight();
        ((AVLNode) d).updateHeight();
    }

    private AVLNode cast(Node node) {
        return (AVLNode) node;
    }

    private AVLNode getTallerChild(AVLNode node) {
        int r = node.getRightHeight();
        int l = node.getLeftHeight();
        return (AVLNode) (r > l ? node.right : node.left);
    }

    private void updateHeight(Node<E> node) {
        ((AVLNode) node).updateHeight();
    }

    protected boolean isBalanced(Node<E> node) {
        return ((AVLNode) node).isBalanced();
    }

    @Override
    protected Node<E> createNode(E element,Node<E> parent) {
        return new AVLNode(element,parent);
    }

    protected static class AVLNode extends Node {
        int height = 1;

        AVLNode(Object element,Node parent) {
            super(element,parent);
        }

        void updateHeight() {
            int r = getRightHeight();
            int l = getLeftHeight();
            height = 1 + Math.max(r,l);
        }

        int getLeftHeight() {
            return left == null ? 0 : ((AVLNode) left).height;
        }

        int getRightHeight() {
            return right == null ? 0 : ((AVLNode) right).height;
        }

        int balanceFactor() {
            int r = getRightHeight();
            int l = getLeftHeight();
            return Math.abs(r - l);
        }

        boolean isBalanced() {
            return balanceFactor() <= 1;
        }

        @Override
        public String toString() {
            return element.toString() + "(" + (parent == null ? "null" : parent.element) + ")";
        }
    }

    public static void main(String[] args) {
        AVLTree tree = new AVLTree();
        for (int i = 0; i < 50; i++) {
            tree.add(i);
        }
        BinaryTrees.println(tree);

    }
}
複製程式碼
RBTree
package top.zhenganwen.learn.algorithm.datastructure.tree;

import top.zhenganwen.learn.algorithm.commons.printer.BinaryTrees;

import java.util.Comparator;
import java.util.Optional;

/**
 * @author zhenganwen
 * @date 2019/11/28/028 15:52
 */
public class RBTree<E> extends BBST<E> {

    private static boolean RED   = false;
    private static boolean BLACK = true;

    public RBTree() {

    }

    public RBTree(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    /**
     * adjustment after bst's insertion.
     * </hr>
     * the node must be leaf node,and we can regard it as insert a element into a 2-3-4 b-tree.</br>
     * the 2-3-4 b-tree's leaf node must but only have one black node. </br>
     * so the b-tree's leaf node can have four situations below:
     * <ul>
     *     <li>one black node with two red children. R<-B->R </li>
     *     <li>one black node with one red left child. R<-B- </li>
     *     <li>one black node with one red right child. -B->R </li>
     *     <li>one black node. -B- </li>
     * </ul>
     *
     * 1. the insert node is added into the left of right of the black node.
     *
     *          B-      => R<-B- / -B->R
     *          <-B-    => R<-B->R
     *          B->R    => R<-B->R
     *
     *         insert into directly with bst's insertion logic
     *
     * 2. the insert node is added into the left of right of the red node. after insertion,the overflow not occurs
     *
     *               R<-B-    =>      R<-B        R<-B
     *                                 \         /
     *                                  R       R
     *
     *               -B->R    =>      -B->R       -B->R
     *                                  /              \
     *                                 R                R
     *
     *          after insertion of bst,we need rotate to let the mid node become the black node
     *
     * 3. the insert node is added into the left of right of the red node. and then,the overflow occurs
     *
     *              R<-B->R  =>      R<-B->R         R<-B->R         R<-B->R          R<-B->R
     *                             /                        \         \                   /
     *                            R                          R         R                 R
     *
     *          let the left and right become two independent b-tree node(color it to black),and then
     *          color itself to red to become a insertion node added its parent b-tree node
     * @param node
     */
    @Override
    protected void afterAdd(Node<E> node) {
        // the insert node is root node or node overflows to top
        if (node.parent == null) {
            darken(node);
            return;
        }
        // the node is leaf node
        RBNode<E> parent = (RBNode<E>) node.parent;
        // 1. black parent
        if (parent.color == BLACK) {
            // redden it -> default red color
            return;
        }
        // 2. red parent,and grand must exist
        RBNode<E> uncle = sibling(parent);
        RBNode<E> grand = (RBNode<E>) parent.parent;
        if (isRed(uncle)) {
            // 2.1 overflow
            darken(parent);
            darken(uncle);
            redden(grand);
            afterAdd(grand);
            return;
        }
        // 2.2 uncle is null or black
        if (parent.isLeftChildOf(grand)) {
            if (node.isLeftChildOf(parent)) {
                // LL
                darken(parent);
                redden(grand);
            } else {
                // LR
                darken(node);
                redden(grand);
                rotateLeft(parent);
            }
            rotateRight(grand);
        } else {
            if (node.isLeftChildOf(parent)) {
                // RL
                darken(node);
                redden(grand);
                rotateRight(parent);
            } else {
                // RR
                redden(grand);
                darken(parent);
            }
            rotateLeft(grand);
        }
    }

    /**
     * 首先,真正被刪除的bst節點肯定存在於該紅黑樹等價的4階B樹的葉子節點中:
     *
     *      a. R_1 <- B_2 -> R_3
     *
     *      b. R_1 <- B_2
     *
     *      c. B_2 -> R_3
     *
     *      d. B_2
     *
     * 1. 如果被刪除的節點是紅節點,如上例 a,c 的 R_1 和 R_3,那麼在bst的刪除邏輯之後不需要額外的修復操作
     *
     * 2. 如果被刪除的節點是黑色節點,如上例 b,c 中的 B_2
     *
     *      2.1 首先不要囊括 a 中的 B_2,這種情況我們會刪除其後繼節點 R_3
     *
     *      2.2 如果刪除 b,c 中的 B_2, bst的刪除邏輯是 用其孩子節點 R_1/R_3 替換它,這時我們為了保證等價性
     *      (B樹節點中必須有且僅有一個黑色節點),需要將該紅色孩子節點染成黑色
     *
     * 3. 如果被刪除的節點沒有紅色孩子節點(即替換節點為null)
     *
     *      3.1 如果被刪除節點的兄弟節點是黑色節點
     *
     *          3.1.1 如果【兄弟節點有紅色孩子節點可以借】,則通過旋轉操作修復紅黑樹
     *
     *                如果兄弟和其紅色孩子節點的相對方位是 LL 或 RR,則對父節點進行 右旋 或 左旋,
     *                並將旋轉後的中間節點【繼承父節點的顏色】、【中間節點的兩個孩子染成黑色】
     *
     *                e.g: delete B_4
     *
     *                          R_3                        R_2
     *                         /   \     =>               /   \
     *                R_1 <- B_2   B_4                 R_1     B_3
     *
     *                如果兄弟和其紅色孩子節點的相對方位是 LR 或 RL,則先將其轉變為 LL 或 RR 的情況後
     *                再複用上述的處理邏輯
     *
     *                e.g: delete B_5
     *
     *                     R_4                           R_4                      R_3
     *                   /     \        =>             /     \      =>          /     \
     *                B_2->R_3  B_5             B_2->R_3     B_5              B_2     B_4
     *
     *          3.1.2 如果兄弟節點沒有紅色孩子可以借,則考慮4階B樹的【下溢】,等價修復紅黑樹
     *
     *                【將父節點拉下來】與當前節點和兄弟節點合併成一個新的4階B樹節點(實際做法是將父節點染黑,兄弟節點染紅)
     *                考慮【下溢】有向上傳播性,我們將父節點作為刪除後節點遞迴執行修復邏輯
     *
     *                e.g: delete B_8
     *                
     *                           B_5                            B_5                           
     *                         /    \                         /    \                             
     *                     B_3       B_7        =>        B_3       \        =>           B_3   <-  B_5    
     *                   /    \     /   \               /    \       \                  /    \       \   
     *                B_2    B_4   B_6  B_8          B_2    B_4  R_6<-B_7            B_2    B_4  R_6<-B_7
     *
     *                        B_8的兄弟節點B_6沒有紅色孩子節點          B_7的兄弟節點B_3沒有紅色孩子節點              下溢到了根節點,終止
     *         
     *      3.2 如果被刪除節點的兄弟節點是紅色節點
     *          
     *          根據紅黑色的性質,等價的4階B樹中,被刪除節點和兄弟節點並不處於同一層中
     *          (兄弟節點和父節點位於一個B樹節點中,被刪除節點位於該B樹節點的下一層的B樹節點中)
     *
     *          那麼兄弟節點肯定有兩個黑色孩子節點,與被刪除節點位於同一層,可以通過旋轉轉換成 3.1
     *
     *
     *          e.g: delete B_7
     *
     *              R_4 <- B_6                  B_4 -> R_6
     *            /    \      \      =>       /      /    \
     *          B_3    B_5    B_7           B_3    B_5    B_7
     *
     *               通過對R_6右旋,B_7的兄弟節點由紅色的R_4轉換成了黑色的B_5,此後可複用 3.1 的邏輯
     *
     *
     *
     * @param node
     * @param replacement
     */
    @Override
    protected void afterRemove(Node<E> node,Node<E> replacement) {
        // 1. 如果被刪除節點是紅色節點
        if (isRed(node)) {
            return;
        }
        // 2. 如果替代被刪除節點的是紅色節點
        if (isRed(replacement)) {
            darken(replacement);
            return;
        }
        if (node.parent == null) {  // 3.1.2 中的下溢傳播到根節點終止
            return;
        }

        Node<E> parent = node.parent;
        boolean left = parent.left == null || parent.left == node; // 當前被刪除節點是否是左子節點 或 上溢傳播至的節點是否是左子節點
        RBNode<E> sibling = (RBNode<E>) (left ? parent.right : parent.left);

        // 3.2 如果兄弟節點是紅色節點,則旋轉父節點,轉變為 3.1
        if (isRed(sibling)) {
            if (left) rotateRight(parent);
            else rotateLeft(parent);
            afterRemove(node,null);
        // 3.1 兄弟節點是黑色節點
        } else if (hasRedChild(sibling)) {
            // 3.1.1 兄弟節點有紅色孩子可以借,將旋轉後的中間節點繼承父節點顏色,兩邊節點染黑
            darken(parent); // 父節點不會成為中間節點,直接提前染黑
            if (sibling.isLeftChildOf(parent)) {
                if (isRed(sibling.left)) {
                    // LL 兄弟節點將成為中間節點
                    if (isRed(parent)) {
                        redden(sibling);
                    }
                    darken(sibling.left);
                } else {
                    // LR 兄弟節點的孩子將成為中間節點
                    if (isRed(parent)) {
                        redden(sibling.right);
                    }
                    darken(sibling);
                    rotateLeft(sibling); // 調整 LR 為 LL
                }
                // 到這裡肯定是 LL
                rotateRight(parent);
            } else {
                // 與上述對稱
                if (isRed(sibling.left)) {
                    // RL
                    if (isRed(parent)) {
                        redden(sibling.left);
                    }
                    darken(sibling);
                    rotateRight(sibling);
                } else {
                    // RR
                    if (isRed(parent)) {
                        redden(sibling);
                    }
                    darken(sibling.right);
                }
                rotateLeft(parent);
            }
        } else {
            // 3.1.2 兄弟節點沒有紅色孩子可以借,父節點染黑、兄弟節點染紅,下溢傳播(如果拉下來的父節點是黑色)
            boolean parentColor = ((RBNode<E>) parent).color;
            darken(parent);
            redden(sibling);
            if (parentColor == BLACK) {
                afterRemove(parent,null);
            }
        }
    }

    private boolean hasRedChild(RBNode<E> rbNode) {
        return rbNode != null && (isRed(rbNode.left) || isRed(rbNode.right));
    }

    private RBNode<E> color(Node<E> node,BLACK);
    }

    /**
     * if the {@code node} is null or its color is black,it will return false
     * @param node
     * @return
     */
    private boolean isRed(Node<E> node) {
        return node != null && ((RBNode<E>) node).color == RED;
    }

    private boolean isBlack(Node<E> node) {
        // node is leaf's children or non-null black node
        return node == null || ((RBNode<E>) node).color == BLACK;
    }

    private RBNode<E> sibling(Node<E> node) {
        if (node.parent == null) {
            return null;
        }
        if (node.isLeftChildOf(node.parent)) {
            return (RBNode<E>) node.parent.right;
        } else {
            return (RBNode<E>) node.parent.left;
        }
    }

    @Override
    protected Node<E> createNode(E element,parent);
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            Optional.ofNullable(left).ifPresent(p -> sb.append(p.element).append("-"));
            if (color == RED) {
                sb.append("R_");
            }
            sb.append(element);
            Optional.ofNullable(right).ifPresent(p -> sb.append("-").append(p.element));
            Optional.ofNullable(parent).ifPresent(p -> sb.append("(").append(p.element).append(")"));
            return sb.toString();
        }
    }

    public static void main(String[] args) {
        Integer[] arr = new Integer[]{
                89,82
        };
        RBTree<Integer> rbt = new RBTree<>();
        for (Integer i : arr) {
            rbt.add(i);
//            System.out.println("add 【" + i + "】");
//            BinaryTrees.println(rbt);
//            System.out.println("=================================================");
        }
        BinaryTrees.println(rbt);
        System.out.println("=================================================");

        for (Integer i : arr) {
            rbt.remove(i);
            System.out.println("remove 【" + i + "】");
            BinaryTrees.println(rbt);
            System.out.println("=================================================");
        }
    }
}
複製程式碼

為何平衡

紅黑樹的5條性質能夠保證其等價於一棵4階B樹

11-29-59.png

效能對比

AVL樹

  • 平衡標準比較嚴格:每個左右子樹的高度差不超過1

  • 最大高度是 1.44 ∗ log 2 n + 2 − 1.328 (100W個節點,AVL樹最大樹高28)

  • 搜尋、新增、刪除都是 O(logn) 複雜度,其中新增僅需 O(1) 次旋轉調整、刪除最多需要 O(logn) 次旋轉調整

    紅黑樹

  • 平衡標準比較寬鬆:沒有一條路徑會大於其他路徑的2倍

  • 最大高度是 2 ∗ log 2 (n + 1) ( 100W個節點,紅黑樹最大樹高40)

  • 搜尋、新增、刪除都是 O(logn) 複雜度,其中新增、刪除都僅需 O(1) 次旋轉調整

據統計,紅黑樹刪除節點發生上溢傳播的次數不會超過3次,因此可認為旋轉調整複雜度為O(1)

技術選型

  • 搜尋的次數遠遠大於插入和刪除,選擇AVL樹;搜尋、插入、刪除次數幾乎差不多,選擇紅黑樹

  • 相對於AVL樹來說,紅黑樹犧牲了部分平衡性以換取插入/刪除操作時少量的旋轉操作,整體來說效能要優於AVL樹

  • 紅黑樹的平均統計效能優於AVL樹,實際應用中更多選擇使用紅黑樹