红黑树
二叉查找树
情况1
此时查找的时间复杂度为 O(logn)
情况2
此时查找的时间复杂度为 O(n), 不符合二叉查找树的设计的初衷。所以引出了平衡二叉树,左右子树高度差不超过1,可以避免出现线性结构的情况,但是还不够理想。
为什么有了AVL树还会有红黑树
因为AVL树要求左右子树高度差不超过1,这个要求太严格 ,导致插入、删除时
,很大几率破坏高度差为1的规则,进而需要通过左旋、右旋来调整,导致性能大打折扣。
为了解决这个问题,就出现了红黑树。
红黑树
底层数据结构就是一个特殊的二叉查找树
特征:
- 每个节点不是黑色就是红色
- 不可能有连在一起的红色节点
- 根节点都是黑色
- 每个红色节点的两个子节点都是黑色,叶子节点都是黑色的
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点
为了满足红黑树的性质,有三种操作
- 改变颜色
- 左旋
- 右旋
插入
插入节点必须为红色, 保证不破坏黑色数量平衡
情景1, 为空树
插入节点为根节点,并设置为黑色
情景2, 插入节点的key已存在
更新节点
情景3: 插入节点的父节点为黑色
直接插入
情景4: 插入节点的父节点为红色
根据红黑树性质,既然父节点为红色 ,则爷爷节点必为黑色
情景4.1: U存在且U为红色
1. P和U改为黑色
2. PP改为红色
3. 将PP设置为当前节点,进行后续处理
情景4.2: U不存在或为黑色 ,插入节点的P是PP的左子节点
情景4.2.1: currnet为P的左子节点(LL红)
1.P设为黑色 ,PP为红色
2.对PP右旋
情景4.2.2: currnet为P的右子节点(LR红)
1.对P左旋
2.P设为current,得到LL红情况
3.按照LL红的情况处理
情景4.3: U不存在或为黑色 ,插入节点的P是PP的右子节点
情景4.3.1: currnet为P的右子节点(RR红)
1.P设为黑色 ,PP为红色
2.对PP左旋
情景4.3.2: currnet为P的左子节点(RL红)
1.对P右旋
2.P设为current,得到RR红情况
3.按照RR红的情况处理
代码
RBTree.java
/*
* 1.创建RBTree,定义颜色
* 2.创建RBNode
* 3.辅助方法定义 parentOf(node), isRed(node), setRed(node), setBlack(node), inorderPrint(node)
* 4.左旋方法 leftRotate(node)
* 5.右旋方法 rightRotate(node)
* 6.公开插入接口 insert(K key, V value)
* 7.内部插入接口 insert(RBNode node)
* 8.修正平衡术插入导致的失衡 insertFixUp(RBNode)
* 9.测试正确性
*/
public class RBTree<K extends Comparable<K>, V> {
private final static boolean RED = true;
private final static boolean BLACK = false;
private RBNode root = null;
public RBNode getRoot() {
return root;
}
private RBNode parentOf(RBNode node) {
if (node != null) {
return node.parent;
}
return null;
}
private boolean isRed(RBNode node) {
if (node != null) {
return node.color == RED;
}
return false;
}
private boolean isBlack(RBNode node) {
if (node != null) {
return node.color == BLACK;
}
return false;
}
private void setRed(RBNode node) {
if (node != null) {
node.color = RED;
}
}
private void setBlack(RBNode node) {
if (node != null) {
node.color = BLACK;
}
}
public void inorderPrint(RBNode node) {
if (node != null) {
inorderPrint(node.left);
System.out.println("Key: " + node.key + ", value:" + node.value);
inorderPrint(node.right);
}
}
private void leftRotate(RBNode x) {
RBNode y = x.right;
x.right = y.left;
if (y.left != null) {
y.parent = x;
}
y.parent = x.parent;
if (y.parent == null) {
root = y;
} else {
if (x == x.parent.left) {
x.parent.left = y;
} else {
x.parent.right = y;
}
}
y.left = x;
x.parent = y;
}
private void rightRotate(RBNode y) {
RBNode x = y.left;
y.left = x.right;
if (x.right != null) {
x.right.parent = y;
}
x.parent = y.parent;
if (x.parent == null) {
root = x;
} else {
if (y == y.parent.left) {
y.parent.left = x;
} else {
y.parent.right = x;
}
}
x.right = y;
y.parent = x;
}
public void insert(K key, V value) {
RBNode node = new RBNode();
node.setValue(value);
node.setKey(key);
node.setColor(RED);
insert(node);
}
private void insert(RBNode node) {
// 查找当前node父节点
RBNode parent = null;
RBNode x = this.root;
while (x != null) {
parent = x;
int cmp = node.key.compareTo(x.key);
if (cmp < 0) {
x = x.left;
} else if (cmp > 0) {
x = x.right;
} else {
x.setValue(node.value);
return;
}
}
node.parent = parent;
if (parent != null) {
int cmp = node.key.compareTo(parent.key);
if (cmp < 0) {
parent.left = node;
} else {
parent.right = node;
}
} else {
this.root = node;
}
insertFixUp(node);
}
private void insertFixUp(RBNode node) {
this.root.setColor(BLACK);
RBNode parent = parentOf(node);
RBNode gParent = parentOf(parent);
if (parent != null && isRed(parent)) {
RBNode uncle = null;
if (parent == gParent.left) { // P是PP的左子节点
uncle = gParent.right;
// 4.1 U存在且U为红色
if (uncle != null && isRed(uncle)) {
//1. P和U改为黑色
//2. PP改为红色
//3. 将PP设置为当前节点,进行后续处理
setBlack(parent);
setBlack(uncle);
setRed(gParent);
insertFixUp(gParent);
return;
}
// 情景4.2: U不存在或为黑色
if (uncle == null || isBlack(uncle)) {
// 情景4.2.1: currnet为P的左子节点(LL红)
//1.P设为黑色 ,PP为红色
//2.对PP右旋
if (node == parent.left) {
setBlack(parent);
setRed(gParent);
rightRotate(gParent);
return;
}
// 情景4.2.2: currnet为P的右子节点(LR红)
//1.对P左旋
//2.P设为current,得到LL红情况
//3.按照LL红的情况处理
if (node == parent.right) {
leftRotate(parent);
insertFixUp(parent);
return;
}
}
} else { // 插入节点的P是PP的右子节点
uncle = gParent.left;
// 4.1 U存在且U为红色
if (uncle != null && isRed(uncle)) {
//1. P和U改为黑色
//2. PP改为红色
//3. 将PP设置为当前节点,进行后续处理
setBlack(parent);
setBlack(uncle);
setRed(gParent);
insertFixUp(gParent);
return;
}
// U不存在或为黑色
if (uncle == null || isBlack(uncle)) {
//情景4.3.1: currnet为P的右子节点(RR红)
//1.P设为黑色 ,PP为红色
//2.对PP左旋
if (node == parent.right) {
setBlack(parent);
setRed(gParent);
leftRotate(gParent);
return;
}
//情景4.3.2: currnet为P的左子节点(RL红)
//1.对P右旋
//2.P设为current,得到RR红情况
//3.按照RR红的情况处理
if (node == parent.left) {
rightRotate(parent);
insertFixUp(parent);
return;
}
}
}
}
}
static class RBNode<K extends Comparable<K>, V> {
private RBNode parent;
private RBNode left;
private RBNode right;
private boolean color;
private K key;
private V value;
public RBNode() {
}
public RBNode(RBNode parent, RBNode left, RBNode right, K key, V value) {
this.parent = parent;
this.left = left;
this.right = right;
this.key = key;
this.value = value;
}
public RBNode getParent() {
return parent;
}
public void setParent(RBNode parent) {
this.parent = parent;
}
public RBNode getLeft() {
return left;
}
public void setLeft(RBNode left) {
this.left = left;
}
public RBNode getRight() {
return right;
}
public void setRight(RBNode right) {
this.right = right;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public boolean getColor() {
return this.color;
}
public boolean isColor() {
return color;
}
public void setColor(boolean color) {
this.color = color;
}
}
}
TreeOperation.java
// TreeOperation.java
public class TreeOperation {
/*
Example of the structure of the tree:
1
/ \
2 3
/ \ / \
4 5 6 7
*/
// the number of layers used to get the tree
public static int getTreeDepth(RBTree.RBNode root) {
return root == null ? 0 : (1 + Math.max(getTreeDepth(root.getLeft()), getTreeDepth(root.getRight())));
}
private static void writeArray(RBTree.RBNode currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
// Ensure that the input tree is not empty
if (currNode == null) return;
// First save the current node to a two-dimensional array
res[rowIndex][columnIndex] = String.valueOf(currNode.getKey()) + "-" + (currNode.isColor() ? "R": "B" ) + "";
// Calculate the current layer in the tree
int currLevel = ((rowIndex + 1) / 2);
// If it reaches the last level, it will return
if (currLevel == treeDepth) return;
// Calculate the interval between each element from the current line to the next line (the interval between the column index of the next line and the column index of the current element)
int gap = treeDepth - currLevel - 1;
// judge the left son, if there is a left son, record the corresponding "/" and the value of the left son
if (currNode.getLeft() != null) {
res[rowIndex + 1][columnIndex - gap] = "/";
writeArray(currNode.getLeft(), rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
}
// Make a judgment on the right son. If there is a right son, record the value of the corresponding "\" and the right son.
if (currNode.getRight() != null) {
res[rowIndex + 1][columnIndex + gap] = "\\";
writeArray(currNode.getRight(), rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
}
}
public static void show(RBTree.RBNode root) {
if (root == null) System.out.println("EMPTY!");
// Get the depth of the tree
int treeDepth = getTreeDepth(root);
// The width of the last line is 2 (n - 1) and the power is 3, plus 1
// as the width of the entire two-dimensional array
int arrayHeight = treeDepth * 2 - 1;
int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
// Use an array of strings to store the elements that should be displayed at each location
String[][] res = new String[arrayHeight][arrayWidth];
// Initialize the array, the default is a space
for (int i = 0; i < arrayHeight; i ++) {
for (int j = 0; j < arrayWidth; j ++) {
res[i][j] = " ";
}
}
// Recursive processing of the entire tree from the root node
// res[0][(arrayWidth + 1)/ 2] = (char)(root.val + '0');
writeArray(root, 0, arrayWidth/ 2, res, treeDepth);
// At this point, all the elements that need to be displayed have been stored in a two-dimensional array, spliced and printed.
for (String[] line: res) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < line.length; i ++) {
sb.append(line[i]);
if (line[i].length() > 1 && i <= line.length - 1) {
i += line[i].length() > 4 ? 2: line[i].length() - 1;
}
}
System.out.println(sb.toString());
}
}
}
MyTest,java
import java.util.Scanner;
public class MyTest {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
RBTree<Integer, Object> rbt = new RBTree();
while (true) {
System.out.println("Please input key");
String key = scanner.next();
rbt.insert(new Integer(key), null);
TreeOperation.show(rbt.getRoot());
}
}
}