2-3查找树定义
1.任意空链接到根节点的路径长度都是相等的
2. 4-结点变换为3-结点时, 树的高度不会发生变化, 只有当根节点是临时的4-结点时, 分解根节点后, 树的高度+1
3.普通二叉查找树是自顶向下生长的, 2-3查找树的自底向上生长的
都是2-3查找树
(插入节点过程中产生的4-结点是临时的!)
红黑树定义
在满足2-3查找树的前提上, 使用对结点间链接的==“标记” == 以
颜色有两种:
红色 模拟2-3查找树中的3-结点
黑色 普通的结点链接形式
性质:
- 红链接均为左连接
2.没有任何一个结点同时与两个红链接相连(4-结点不存在)
3.任意空链接到根节点的路径经过相同的黑链接
这样, 红黑树能够在插入结点的同时保持美妙的平衡.
有了这些知识, 开始构建自己的红黑树吧!
左旋和右旋
当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋.
比如下图中, 插入一个值为50的结点
通过左旋. 我们能使只有左链接为红色,
但是
会出现下面的情况:
这违反了红黑树的规则1, 所以我们仍要操作, 那么进行右旋
|
|
|
但是发现, 还是违反了规则2
所以继续操作
颜色反转
上述的操作产生了有两个红链接的结点(相当于二叉树中的4-结点), 此时, 我们需要对此结点继续进行操作, 使其满足规则2.
先看看下面这张图
插入S后, 形成了AES(4-结点), 然后对4-结点进行分解
和这张图
此时再来看看这张图: 是不是有相同的地方(把它看成上面的4-结点)
现在继续对这个结点进行操作:
看到这里, 你可能有些疑惑: 怎么就是把两个红链接变为黑色呢?
别急, 看下面
假如有一个50元素的结点时
结果不一样了, 这是因为我们规定: 根结点的链接永远指向自己, 根链接永远为黑色
看到这里, 代码要实现的就是找到要插入元素的位置, 并放置该结点, 并先后进行**(左旋, 右旋, 颜色反转)------(如果必要的话)** 就能保持红黑树的平衡了
代码:
Java实现代码
package cn.datastructure.structure.tree;
/**
* @author 悠一木碧
* 红黑树实现:
* 基于了2-3查找树的思想:
* 任意空链接到根节点的路径长度都是相等的
* 4-结点变换为3-结点时, 树的高度不会发生变化, 只有当根节点是临时的4-结点时, 分解根节点后, 树的高度+1
* 普通二叉查找树是自顶向下生长的, 2-3查找树的自底向上生长的
* 且:
* 红链接均为左连接
* 没有任何一个结点同时与两个红链接相连
* 任意空链接到根节点的路径经过相同的黑链接
*/
public class RedBlackTree<Key extends Comparable<Key>, Value> {
private Node root;
private int size;
private final boolean RED = true;
private final boolean BLACK = false;
private class Node{
public Key key;
public Value value;
public Node left;
public Node right;
// 用来标识父节点指向自己链接的颜色
public 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;
}
public Node() {
}
}
// 将根链接的颜色永远设置为黑色
public void put(Key key, Value value){
root = put(root, key, value);
this.root.color = BLACK;
}
// 在node结点下完成插入操作, 是个递归方法
private Node put(Node node, Key key, Value value){
if(null == node){
size++;
return new Node(key, value, null, null, BLACK);
}
if(key.compareTo(node.key) < 0){
node.left = put(node.left, key, value);
} else if(key.compareTo(node.key) > 0){
node.right = put(node.right, key, value);
} else{
node.value = value;
}
if(isRed(node.right) && !isRed(node.left)){
rotateLeft(node);
}
if(isRed(node.left) && isRed(node.left.left)){
rotateRight(node);
}
if(isRed(node.left) && isRed(node.right)){
colorInversion(node);
}
return node;
}
/**
* 左旋操作,
* @param head
*/
private Node rotateLeft(Node head){
Node rightSon = head.right;
head.right = rightSon.left;
rightSon.left = head;
rightSon.color = head.color;
head.color = RED;
return rightSon;
}
/**
* 右旋操作
* @param head
* @return
*/
private Node rotateRight(Node head){
Node leftSon = head.left;
head.left = leftSon.right;
leftSon.right = head;
leftSon.color = head.color;
head.color = RED;
return leftSon;
}
/**
* 空链接默认为黑色
* @param node
* @return
*/
private boolean isRed(Node node){
if(null == node){
return false;
}
return node.color == RED;
}
/**
* 相当于拆分掉4-结点, 在红黑树中, 临时的4-结点相当于当前结点的左右链接都是红色
* @param node
*/
private void colorInversion(Node node){
node.color = RED;
node.left.color = BLACK;
node.right.color = BLACK;
}
public Value get(Key key){
return get(root, key);
}
private Value get(Node node, Key key){
if(null == node){
return null;
}
int res = key.compareTo(node.key);
if(res > 0){
return get(node.right, key);
} else if(res < 0){
return get(node.left, key);
} else{
return node.value;
}
}
public int size(){
return size;
}
}