通過java簡單實現紅黑樹
package com.example.demo.rbt;
import lombok.Data;
public class RBTree<K extends Comparable<K>, V> {
/**
* 紅色
*/
public static final boolean RED = true;
/**
* 黑色
*/
public static final boolean BLACK = false;
/**
* 根節點
*/
private RBNode<K, V> root;
/**
* 獲取父節點
* @param node 當前節點
* @return 父節點
*/
private RBNode<K, V> getParentNode(RBNode<K, V> node){
if (node != null) {
return node.parent;
}
return null;
}
/**
* 判斷節點是否是紅色
* @param node 當前節點
* @return true or false
*/
private boolean isRed(RBNode<K, V> node){
return node != null && node.color;
}
/**
* 判斷節點是否是黑色
* @param node 當前節點
* @return true or false
*/
private boolean isBlack(RBNode<K, V> node){
return node != null && !node.color;
}
/**
* 設置爲紅色
* @param node 當前節點
*/
private void setRed(RBNode<K, V> node){
if (node != null) {
node.setColor(RED);
}
}
/**
* 設置爲黑色
* @param node 當前節點
*/
private void setBlack(RBNode<K, V> node){
if (node != null) {
node.setColor(BLACK);
}
}
/**
* 右旋:以某個節點作爲支點(旋轉節點),其左子節點變爲旋轉節點的父節點,左子節點的右子節點變爲旋轉節點的左子節點,右子節點保持不變
* F F
* | |
* K (旋轉節點) H
* / \ ==> / \
* H M NIL K
* / \ / \
* NIL NIL NIL M
* @param node 旋轉節點
*/
private void rightSpin(RBNode<K, V> node){
// 當前節點的父節點 F
RBNode<K, V> pN = node.parent;
// 當前節點的左子節點 H
RBNode<K, V> lN = node.left;
// 將K的左子節點指向H的右子節點NIL,將H的右子節點的父節點更新爲K K--NIL
node.left = lN.right;
if (lN.right != null) {
lN.right.parent = node;
}
// 將K的父節點更新爲H,將H的右子節點更新爲K H--K
node.parent = lN;
lN.right = node;
// 當F不爲空時(如果K是根節點就有可能爲null),將H的父節點更新爲F,將F的左(右)子節點更新爲H H--F
if (pN != null) {
lN.parent = pN;
if (pN.left == node) {
pN.left = lN;
}else {
pN.right = lN;
}
}else {
// 將H設置爲根節點
this.root = lN;
}
}
/**
* 左旋:以某個節點作爲支點(旋轉節點),其右子結點變爲旋轉節點的父節點,右子結點的左子節點變爲旋轉節點的右子節點,左子節點保持不變
* F F
* | |
* K (旋轉節點) M
* / \ ==> / \
* H M K NIL
* / \ / \
* NIL NIL H NIL
* @param node 旋轉節點
*/
private void leftSpin(RBNode<K, V> node){
// 當前節點的父節點 F
RBNode<K, V> pN = node.parent;
// 當前節點的右子節點 M
RBNode<K, V> rN = node.right;
// 將K的右子節點指向M的左子節點NIL,將M的左子節點的父節點更新爲K K--NIL
node.right = rN.left;
if (rN.left != null) {
rN.left.parent = node;
}
// 將K的父節點更新爲M,將M的左子節點更新爲K K--M
node.parent = rN;
rN.left = node;
// 當F不爲空時(如果K是根節點就有可能爲null),將M的父節點更新爲F,將F的左(右)子節點更新爲M M--F
rN.parent = pN;
if (pN != null) {
// 如果K原先是左節點則將F的左節點更新爲M,如果是右節點則將F的右節點更新爲M
if (pN.left == node) {
pN.left = rN;
}else {
pN.right = rN;
}
}else {
// 將M設置爲根節點
this.root = rN;
}
}
/**
* 中序遍歷
*/
public void inOrderPrint(){
System.out.print("中序遍歷:");
inOrderPrint(this.root);
}
public RBNode<K, V> getRoot(){
return this.root;
}
private void inOrderPrint(RBNode<K, V> node){
if (node != null) {
inOrderPrint(node.left);
System.out.print(node.key + " ");
inOrderPrint(node.right);
}
}
public RBNode<K, V> get(K key){
return getNode(this.root, key);
}
private RBNode<K, V> getNode(RBNode<K, V> node, K key){
while (node != null){
K k = node.key;
if (key.compareTo(k) > 0) {
node = node.right;
}else if(key.compareTo(k) < 0){
node = node.left;
}else {
return node;
}
}
return null;
}
public void add(K key, V value){
RBNode<K, V> node = new RBNode<>(RED, key, value);
addNode(node);
}
private void addNode(RBNode<K, V> node){
// 查找當前節點的父節點,從root開始
RBNode<K, V> parent = null;
RBNode<K, V> temp = this.root;
while (temp != null){
parent = temp;
K k = temp.key;
int i = node.key.compareTo(k);
if (i > 0) {
// 插入節點的Key大於當前節點的Key,從當前節點的右子樹找
temp = temp.right;
} else if (i < 0) {
// 插入節點的Key小於當前節點的Key,從當前節點的左子樹找
temp = temp.left;
} else {
// 插入節點的Key等於當前節點的Key,直接替換value TODO 場景二
temp.value = node.value;
return;
}
}
// 設置插入節點的父節點
node.parent = parent;
if (parent != null) {
int i = node.key.compareTo(parent.key);
if (i > 0) {
// 如果插入節點的Key大於父節點的Key,則放在父節點的右子節點上
parent.right = node;
}else {
// 如果插入節點的Key小於父節點的Key,則放在父節點的左子節點上
parent.left = node;
}
}else {
// 說明是顆空樹,將插入節點設置爲根節點 TODO 場景一
this.root = node;
}
// 插入完後走自平衡處理
selfBalance(node);
}
/**
* 插入節點的後續處理
* |--- 1.紅黑樹爲空數,將插入節點設置爲根節點並置爲黑色
* |--- 2.插入節點的Key已存在,則更新已存在節點的Value爲插入節點的Value
* |--- 3.插入節點的父節點是黑色的,則直接插入,不會影響平衡
* |--- 4.插入節點的父節點是紅色的
* |--- 4.1 叔叔節點存在並且爲紅節點
* - 將父節點(F)和叔叔節點(V)設置爲黑色
* - 將祖父節點(P)設置爲紅色
* - 將祖父節點設置爲當前插入節點
* |--- 4.2 叔叔節點不存在或者爲黑節點,並且插入節點的父節點是祖父節點的左子節點
* |--- 4.2.1 插入節點是其父節點的左子節點(LL)
* - 將父節點(F)設置爲黑色
* - 將祖父節點(P)設置爲紅色
* - 對祖父節點(P)進行右旋
* |--- 4.2.1 插入節點是其父節點的右子節點(LR)
* - 對父節點(F)進行左旋
* - 把父節點(F)設置爲插入結點,得到情景4.2.1
* - 進行情景4.2.1的處理
* |--- 4.3 叔叔節點不存在或者爲黑節點,並且插入節點的父節點是祖父節點的右子節點
* |--- 4.3.1 插入節點是其父節點的右子節點(RR)
* - 將父節點(F)設置爲黑色
* - 將祖父節點(P)設置爲紅色
* - 對祖父節點(P)進行左旋
* |--- 4.3.2 插入節點是其父節點的左子節點(RL)
* - 對父節點(F)進行右旋
* - 把父節點(F)設置爲插入結點,得到情景4.3.1
* - 進行情景4.3.1的處理
* @param node
*/
private void selfBalance(RBNode<K, V> node){
// 1.紅黑樹爲空數,將插入節點設置爲根節點並置爲黑色
if (this.root == node) {
setBlack(node);
return;
}
// 2.插入節點的Key已存在,則更新已存在節點的Value爲插入節點的Value
// 3.插入節點的父節點是黑色的,則直接插入,不會影響平衡
// 4.插入節點的父節點是紅色的
// 父節點
RBNode<K, V> pN = getParentNode(node);
// 祖父節點
RBNode<K, V> gpN = getParentNode(pN);
if (isRed(pN)) {
// 叔叔節點
RBNode<K, V> uN;
if (gpN.left == pN) {
uN = gpN.right;
}else {
uN = gpN.left;
}
// 4.1 叔叔節點存在並且爲紅節點
if (isRed(uN)) {
// 將父節點和叔叔節點設置爲黑色,祖父節點設置爲紅色,然後將祖父節點設置爲當前節點進行遞歸
setBlack(pN);
setBlack(uN);
setRed(gpN);
selfBalance(gpN);
return;
}
// 4.2 叔叔節點不存在或者爲黑節點
if (uN == null || isBlack(uN)) {
// 插入節點的父節點是祖父節點的左子節點
if (pN == gpN.left) {
// 4.2.1 插入節點是其父節點的左子節點(LL)
if (node == pN.left) {
setBlack(pN);
setRed(gpN);
rightSpin(gpN);
}else {
// 4.2.2 插入節點是其父節點的右子節點(LR)
leftSpin(pN );
selfBalance(pN);
}
}else {
// 插入節點的父節點是祖父節點的右子節點
if (node == node.parent.left) {
// 4.3.2 插入節點是其父節點的左子節點(RL)
rightSpin(pN);
selfBalance(pN);
}else {
// 4.3.1 插入節點是其父節點的右子節點(RR)
setBlack(pN);
setRed(gpN);
leftSpin(gpN);
}
}
}
}
}
@Data
static class RBNode<K extends Comparable<K>, V>{
/**
* 父節點
*/
private RBNode<K, V> parent;
/**
* 右子節點
*/
private RBNode<K, V> right;
/**
* 左子節點
*/
private RBNode<K, V> left;
/**
* 顏色
*/
private boolean color;
/**
* 鍵
*/
private K key;
/**
* 值
*/
private V value;
public RBNode() {
}
public RBNode(boolean color, K key, V value) {
this.color = color;
this.key = key;
this.value = value;
}
/**
* 這裏重寫toString方法是因爲子節點包含父節點的引用,父節點也包含子節點的引用,
* 在debug的時候會調用toString方法顯示結構,相互引用造成StackOverflowError
*/
@Override
public String toString() {
return "RBNode{" +
"color=" + color +
", key=" + key +
", value=" + value +
'}';
}
}
}
打印紅黑樹結構(網上找的一個方法)
package com.example.demo.rbt;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class RBTreePrinterTest {
public static <K extends Comparable<K>, V> void printNode(RBTree.RBNode<K, V> root) {
int maxLevel = RBTreePrinterTest.maxLevel(root);
printNodeInternal(Collections.singletonList(root), 1, maxLevel);
}
private static <K extends Comparable<K>, V> void printNodeInternal(List<RBTree.RBNode<K, V>> nodes, int level, int maxLevel) {
if (nodes.isEmpty() || RBTreePrinterTest.isAllElementsNull(nodes)) {
return;
}
int floor = maxLevel - level;
int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0)));
int firstSpaces = (int) Math.pow(2, (floor)) - 1;
int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1;
RBTreePrinterTest.printWhitespaces(firstSpaces);
List<RBTree.RBNode<K, V>> newNodes = new ArrayList<>();
for (RBTree.RBNode<K, V> node : nodes) {
if (node != null) {
String key = node.isColor() ? "\033[31;4m" + node.getKey() + "\033[0m" : node.getKey().toString();
System.out.print(key);
newNodes.add(node.getLeft());
newNodes.add(node.getRight());
} else {
newNodes.add(null);
newNodes.add(null);
System.out.print(" ");
}
RBTreePrinterTest.printWhitespaces(betweenSpaces);
}
System.out.println();
for (int i = 1; i <= endgeLines; i++) {
for (RBTree.RBNode<?, ?> node : nodes) {
RBTreePrinterTest.printWhitespaces(firstSpaces - i);
if (node == null) {
RBTreePrinterTest.printWhitespaces(endgeLines + endgeLines + i + 1);
continue;
}
if (node.getLeft() != null) {
System.out.print("/");
} else {
RBTreePrinterTest.printWhitespaces(1);
}
RBTreePrinterTest.printWhitespaces(i + i - 1);
if (node.getRight() != null) {
System.out.print("\\");
} else {
RBTreePrinterTest.printWhitespaces(1);
}
RBTreePrinterTest.printWhitespaces(endgeLines + endgeLines - i);
}
System.out.println("");
}
printNodeInternal(newNodes, level + 1, maxLevel);
}
private static void printWhitespaces(int count) {
for (int i = 0; i < count; i++) {
System.out.print(" ");
}
}
private static <K extends Comparable<K>, V> int maxLevel(RBTree.RBNode<K, V> node) {
if (node == null) {
return 0;
}
return Math.max(RBTreePrinterTest.maxLevel(node.getLeft()), RBTreePrinterTest.maxLevel(node.getRight())) + 1;
}
private static <K extends Comparable<K>, V> boolean isAllElementsNull(List<RBTree.RBNode<K, V>> list) {
for (Object object : list) {
if (object != null) {
return false;
}
}
return true;
}
public static void main(String[] args) {
RBTree<Integer, Object> rbTree = new RBTree<>();
// 隨機15位數插入
new Random().ints(15, 1, 50).forEach(k -> rbTree.add(k, null));
RBTreePrinterTest.printNode(rbTree.getRoot());
rbTree.inOrderPrint();
}
}
實現效果
紅色字體需要安裝IDEA插件 ANSI Highlighter