紅黑樹是2-3-4樹的實現,因此在講紅黑樹以前想講下2-3-4樹有助於理解紅黑樹。 由於紅黑樹是一棵自平衡二叉搜索樹,經過結點顏色改變和局部旋轉來維持平衡,因此除了一些會改變樹結構的操做以外,其餘的操做都和普通的二叉搜索樹相同。所以這裏就只講插入刪除操做。 由於我要用紅黑樹實現一個符號表,因此結點須要存儲鍵值對,並且實現的紅黑樹是基於2-3-4樹。java
實現部分的代碼用Javanode
每一個結點的類型是Node,裏面有5個字段。算法
private class Node {
Key key;
Value value;
Node left;
Node right;
boolean color;
public Node(Key key, Value value, Node left, Node right, boolean color) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.color = color;
}
}
複製代碼
當咱們想要在樹中插入一個新結點時,先在樹中搜索與插入結點鍵相同的結點。dom
Value
字段就完成了。若是插到2-結點下,因爲2-結點是黑色結點則不會破壞紅黑樹的任何性質,因此不用作任何操做就完成了。性能
若是插到3-結點下,從上面3-結點的圖看,3-結點有三個位置能夠插入。測試
若是插入黑色結點的位置下則變成4-結點也不用作任何操做就完成了。ui
若是插到3-結點的紅色結點下,則破壞了紅黑樹的性質4。以下圖新插入的0003
結點,由於插入位置在右邊,則須要對0001
作一個左旋操做: this
若是插入位置在左邊,以下圖新插入的0002
結點。則須要對插入結點的父節點作一個右旋操做,再對0001
作一個左旋操做: spa
不管插到4-結點的哪一個地方都會破壞性質4,這時只要將4-結點分解爲兩個2-結點並將中間結點往上傳給父結點。以下圖新插入的0004
結點: 3d
import java.util.*;
public class RBTree <Key extends Comparable<Key>, Value>{
private class Node {
Key key;
Value value;
Node left;
Node right;
boolean color;
public Node(Key key, Value value, Node left, Node right, boolean color) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.color = color;
}
}
private static final boolean RED = true;
private static final boolean BLACK = false;
private int size;
private Node root;
public boolean isEmpty() {
return root == null;
}
private boolean isRed(Node node) {
return node != null && node.color;
}
//顏色轉換
private void flipColors(Node h) {
h.color = !h.color;
h.left.color = !h.left.color;
h.right.color = !h.right.color;
}
//左旋
private Node rotationLeft(Node node) {
Node x = node.right;
node.right = x.left;
x.left = node;
x.color = node.color;
node.color = RED;
return x;
}
//右旋
private Node rotationRight(Node node) {
Node x = node.left;
node.left = x.right;
x.right = node;
x.color = node.color;
node.color = RED;
return x;
}
//平衡操做
private Node balance(Node node) {
if (isRed(node.left) && isRed(node.right) && !isRed(node)) {
if ((isRed(node.left.left) || isRed(node.left.right) || isRed(node.right.left) || isRed(node.right.right)))
flipColors(node);
}
else {
if (isRed(node.left)){
if (isRed(node.left.right))
node.left = rotationLeft(node.left);
if (isRed(node.left) && isRed(node.left.left))
node = rotationRight(node);
}else if (isRed(node.right)){
if (isRed(node.right) && isRed(node.right.left))
node.right = rotationRight(node.right);
if (isRed(node.right) && isRed(node.right.right))
node = rotationLeft(node);
}
if (isRed(node.left) && isRed(node.right) && !isRed(node)) {
if ((isRed(node.left.left) || isRed(node.left.right) || isRed(node.right.left) || isRed(node.right.right)))
flipColors(node);
}
}
return node;
}
private Node max(Node node) {
if(node == null) {
return null;
} else {
while(node.right != null) {
node = node.right;
}
return node;
}
}
private Node min(Node node) {
if(node == null) {
return null;
} else {
while(node.left != null) {
node = node.left;
}
return node;
}
}
public Value max() {
return root == null ? null : max(root).value;
}
public Value min() {
return root == null ? null : min(root).value;
}
//插入
public void put(Key key, Value value) {
root = put(key, value, root);
root.color = BLACK;
}
private Node put(Key key, Value value, Node node) {
if(node == null) {
++size;
return new Node(key, value, null, null, RED);
} else {
int cmp = key.compareTo(node.key);
if(cmp < 0) {
node.left = put(key, value, node.left);
} else if (cmp > 0){
node.right = put(key, value, node.right);
}else{
node.value = value;
}
return balance(node);
}
}
public void deleteMin(){
if (!isEmpty()){
root.color = RED;
root = deleteMin(root);
--size;
if (!isEmpty())
root.color = BLACK;
}
}
private Node deleteMin(Node node){
if (node.left == null){
return node.right;
}
if (!isRed(node.left)) {
if(!isRed(node.left) && !isRed(node.right))
flipColors(node);
else
node = rotationLeft(node);
}
node.left = deleteMin(node.left);
return balance(node);
}
public void deleteMax(){
if (!isEmpty()){
root.color = RED;
root = deleteMax(root);
--size;
if (!isEmpty())
root.color = BLACK;
}
}
private Node deleteMax(Node node){
if (node.right == null){
return node.left;
}
if (!isRed(node.right)) {
if(!isRed(node.left) && !isRed(node.right))
flipColors(node);
else
node = rotationRight(node);
}
node.right = deleteMax(node.right);
return balance(node);
}
//刪除
public void delete(Key key){
if (!isEmpty()){
root.color = RED;
root = delete(key, root);
if (!isEmpty())
root.color = BLACK;
}
}
private Node delete(Key key, Node node){
if (node == null)
return null;
int cmp = key.compareTo(node.key);
if (cmp < 0){
if (node.left != null && !isRed(node.left)) {
if(!isRed(node.right))
flipColors(node);
else
node = rotationLeft(node);
}
node.left = delete(key, node.left);
}else if (cmp > 0){
if (node.right != null && !isRed(node.right)) {
if(!isRed(node.left))
flipColors(node);
else
node = rotationRight(node);
}
node.right = delete(key, node.right);
}else {
--size;
if (node.left == null)
return node.right;
if (node.right == null)
return node.left;
Node x = min(node.right);
node.key = x.key;
node.value = x.value;
node = delete(x.key, node);
}
return balance(node);
}
//判斷樹是否爲一棵紅黑樹
public boolean isRBTree() {
return isRBTree(root);
}
public boolean isRBTree(Node node) {
if(node == null) {
return true;
} else if(node.color == RED) {
return false;
} else {
Node x = node;
int count = 0;
for(; x != null; x = x.left) {
if(x.color == BLACK) {
++count;
}
}
return isRBTree(node, count, 0);
}
}
private boolean isRBTree(Node node, int count, int k) {
if(node == null) {
return count == k;
} else if((isRed(node.left) && isRed(node.left.left))
||(isRed(node.left) && isRed(node.left.right))
||(isRed(node.right) && isRed(node.right.right))
||(isRed(node.right) && isRed(node.right.left))) {
return false;
} else {
if(node.color == BLACK) {
++k;
}
return node.left == null && node.right == null ? k == count:isRBTree(node.left, count, k) && isRBTree(node.right, count, k);
}
}
//樹的中序遍歷
public void inTraverse(){
inTraverse(root);
}
private void inTraverse(Node node){
if (node == null)
return;
inTraverse(node.left);
System.out.print(node.key + " ");
inTraverse(node.right);
}
//測試
public static void main(String[] args) {
int n = 3000, a;
Random random = new Random();
RBTree<Integer, String> rbt = new RBTree();
for (int i = 1; i <= n; ++i) {
a = random.nextInt(50000);
rbt.put(a, "naoko");
}
for (int i = 0; i < 1500; ++i) {
rbt.delete(i);
}
if (!rbt.isRBTree()) {
System.out.println("不是紅黑樹");
return;
}
rbt.inTraverse();
System.out.print("是紅黑樹");
}
}
複製代碼
紅黑樹和AVL樹相似,都是在進行插入和刪除操做時經過特定操做保持樹的平衡,從而得到較高的查找性能。不一樣的是紅黑樹並非向AVL樹那樣追求完美平衡,而是黑色平衡,即從根結點到任意一個空結點的簡單路徑上黑色結點數都相同。由於一棵紅黑樹的高度最高不超過2lg(N+1)
,所以其查找時間複雜度也是O(lgN)
級別的。而對於插入和刪除操做產生不平衡狀況都會在3次旋轉以內快速解決,因此複雜度基本爲O(lgN)
級別,也由於這一點紅黑樹的效率比AVL樹高。
紅黑樹的插入和刪除操做都有自頂向下和自底向上兩種方法,其中自頂向下較爲容易,個人刪除操做實現屬於自頂向下的方法。在JDK中的TreeMap中插入和刪除就用了自底向上的方法。