原來手寫紅黑樹可以這麼簡單
紅黑樹由4階B樹演化而來,瞭解AVL樹的左旋、右旋和4階B樹的上溢、下溢對我們學習紅黑樹有著事半功倍的效果
多路平衡搜尋樹——B樹
-
每個節點,所有子樹高度相同
-
m階B樹表示節點的子節點個數最多為m,2-3樹就是3階B樹,2-3-4樹就是4階B樹,以此類推
-
節點的元素個數限制:
- 根節點:1 <= x <= m - 1
- 非根節點:(⌈m/2⌉ - 1) <= x <= (m - 1);
-
節點的子節點個數限制:
- 2 <= x <= m
- ⌈m/2⌉ <= x <= m
-
新增元素:新新增的元素必定會新增到葉子節點中
-
上溢:當向某個節點新增元素導致該節點元素個數超出限制
這是唯一一種能讓B樹長高的情況,即新增元素時,上溢傳播到了根節點
-
刪除元素
- 當刪除葉子節點中的元素時,直接移除即可
- 當刪除非葉子節點中的元素時,先將其前驅/後繼元素覆蓋該元素,然後再刪除其前驅/後繼元素
- 某元素的前驅或後繼元素必定在葉子節點中
- 真正的刪除必定發生在葉子節點中
-
下溢:
-
下溢:刪除某元素後,該元素所在節點元素個數變為( ⌊m/2⌋ -2)
-
如果下溢節點有元素個數為(⌊m/2⌋)或以上的同層相鄰節點,可從該節點“借”一個元素
-
將下溢節點A和滿足條件的相鄰節點B中間的父節點元素複製一份新增到A中
-
將B中的最大元素(如果B在A的左邊)或最小元素(如果B在A的右邊)覆蓋父節點元素
-
刪除B中的最大/最小元素
-
-
如果下溢節點的同層相鄰節點元素小於或等於(⌊m/2⌋-1),這時沒辦法借了,則合併下溢節點和任意一個相鄰節點,並將兩者之間的父節點元素也扯下來
- 合併之後的節點元素個數為( ⌊m/2⌋ + ⌊m/2⌋ -2),不超過(m-1)
- 此操作可能會導致父節點下溢,下溢現象可能沿父節點向上傳播
如果下溢調整時,扯下一個父節點元素之後導致父節點沒有元素了,此時樹的高度減一
-
-
依序向4階B樹(2-3-4樹)中新增1~22這22個元素步驟圖:
-
依序刪除22~1
紅黑樹
紅黑樹的性質
上圖中的樹並不是一棵紅黑色,因為綠色路徑上只有兩個黑色節點,而其他紅色路徑有3個黑色節點,不符合性質5
紅黑樹 VS 2-3-4樹
當B樹葉子節點元素分別為:紅黑紅、黑紅、紅黑、黑。(將紅黑樹當做2-3-4樹來看,只有黑色節點才能獨立成為一個B樹節點,且其左右紅色子節點可以融入該B樹節點中)
新增元素
染成紅色新增到葉子節點中
新元素的定位邏輯與BST一致,需要注意的是新增之後的調整邏輯。
分情況討論
1、新增第一個元素
將節點染成黑色
2、新增的節點作為黑色節點的子節點
染成紅色新增即可,無需調整
3、新增的節點作為紅色節點的子節點,但無需上溢
LL/RR,單旋
RL/LR,雙旋
4、新增的節點作為紅色的子節點,且需上溢
LL上溢
RR上溢
LR上溢
RL上溢
程式碼實現
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("=================================================");
}
}
}
複製程式碼
刪除元素
前提
1、刪除紅色節點
2、刪除黑色節點
2.1、刪除有一個紅色孩子的黑色節點
2.2、刪除沒有紅色孩子的黑色節點
2.2.1、被刪除節點的兄弟節點為黑色
2.2.2、被刪除節點的兄弟節點為紅色
程式碼實現
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樹
效能對比
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樹,實際應用中更多選擇使用紅黑樹