1. 简述
红黑树是一种二叉搜索树,具有以下特点:
- 每个节点具有颜色属性,黑色或者红色
- 根节点是黑色的
- 每个叶节点(NIL)是黑色的
- 红色节点的子节点都是黑色的
- 任意节点到其所有叶子节点的路径上黑色节点数相同
- n个节点的红黑树高度至多2lg(n+1),相关操作时间复杂度O(lgn)
注:java中treeSet、treeMap是通过红黑树结构实现的
2. 旋转
/*左旋
将右孩子y旋转到当前节点x,当前节点x及其左子树作为y的左子树
将y的左子树移为x的右子树
*/
public void leftRotate(Node x){
Node y = x.right;
if(y!=nil){
//y的左子树移为x的右子树
x.right = y.left;
if(y.left!=nil)
y.left.father = x;
//y顶替x的位置
y.father = x.father;
if(x.father==nil)
root = y;
else if(x==x.father.left)
x.father.left = y;
else
x.father.right = y;
//x作为y的左子树
y.left = x;
x.father = y;
}
}
/*右旋
将左孩子y旋转到当前节点x,当前节点x及其右子树作为y的右子树
将y的右子树移为x的左子树
*/
public void rightRotate(Node x){
Node y = x.left;
if(y!=nil){
//y的右子树移为x的左子树
x.left = y.right;
if(y.right!=nil)
y.right.father = x;
//y顶替x的位置
y.father = x.father;
if(x.father==nil)
root = y;
else if(x.father.left == x)
x.father.left = y;
else
x.father.right = y;
//x作为y的右子树
y.right = x;
x.father = y;
}
}
3. 插入
public void add(int value){
Node newNode = new Node(value);//创建新节点
Node y = nil;
Node x = root;
while(x!=nil){ //递归查找插入位置
y=x;
if(newNode.value < x.value)
x=x.left;
else
x=x.right;
}
newNode.father = y;
if(y==nil) { //第一个节点,更新根节点
root = newNode;
}
else if(newNode.value < y.value) //判断新增节点作为左孩子还是右孩子
y.left = newNode;
else
y.right = newNode;
newNode.left = nil;
newNode.right = nil;
newNode.color = 'R'; //默认红色
rbInsertFixup(newNode); //更新颜色
}
3.1 修复规则
插入元素后破坏红黑树规则,需要调整红黑树,如下情况
/*规则修复
1.当新增节点的父节点是黑色,则无需调整
2.新增节点为根节点,则改为黑色
3.父节点为红色
a.叔节点为红色,叔、父都调为黑色,爷爷调为红色
b.叔节点为黑色,当前节点为右孩子,和父节点进行左旋
c.叔节点为黑色,当前节点为左孩子,父节点调为黑色,爷节点调为红色,右旋
*/
private void rbInsertFixup(Node node){
while(node.father.color == 'R'){
Node y = null;
//获取叔节点
if(node.father==node.father.father.left)
y = node.father.father.right;
else
y = node.father.father.left;
if(y!=null) {
if (y.color == 'R') {
node.father.color = 'B';
y.color = 'B';
node.father.father.color = 'R';
node = node.father.father; //取爷爷节点向上迭代
} else if (node == node.father.right) {
node = node.father;
leftRotate(node);
} else {
node.father.color = 'B';
node.father.father.color = 'R';
rightRotate(node);
}
}
}
root.color = 'B';
}
4. 删除
/*节点上移
移除节点u,节点v上移
*/
private void transplant(Node u, Node v){
if(u.father == nil)
root = v;
else if(u==u.father.left)
u.father.left = v;
else
u.father.right = v;
if(v!=nil)
v.father = u.father;
}
public void delete(Node z){
Node x;
Node y = z;
char originalColor = y.color;
if(z.left==nil) { //无左孩子(包含无孩子),右孩子上移
x=z.right;
transplant(z, z.right);
}
else if(z.right==nil) { //无右孩子
x=z.left;
transplant(z, z.left); //左孩子上移
}
else{//有两个孩子
//取右孩子树的最小值,最小值没有左孩子,将最小节点提到原先z的位置
y = minimum(z.right);
originalColor = y.color; //最小节点上提,其右子树可能破坏规则
x=y.right;
if(y.father!=z){
transplant(y,y.right);// 最小节点提取出后右孩子树上移
y.right = z.right; //y成为右子树中第一个节点,且没有左孩子
y.right.father = y;
}else {
x.father = y;
}
transplant(z,y); //移除z,y上移
y.left = z.left; //原z的左孩子,变为y的左孩子
y.left.father = y;
y.color=z.color; //最小节点颜色与删除节点保持一致,此处未破坏规则
}
if(originalColor=='B') //只有原节点是黑色才破坏规则
rbDeleteFixup(x);
}
4.1 修复规则
private void rbDeleteFixup(Node x){
while(x!=root && x.color == 'B'){
Node w = null;
//获取兄弟节点
if(x==x.father.left)
w = x.father.right;
else
w = x.father.left;
if(w!=null) {
//兄弟节点是红色
if (w.color == 'R') {
w.color = 'B';
x.father.color = 'R';
if(w == x.father.right) {
leftRotate(x.father);
w = x.father.right;
}else {
rightRotate(x.father);
w = x.father.left;
}
} else { //兄弟节点为黑色
//兄弟节点的两个子节点都是黑色
if (w.left.color == 'B' && w.right.color == 'B') {
w.color = 'R';
x = x.father;
} else {
//兄弟节点左孩子红色,右孩子黑色
if (w.right.color == 'B') {
w.left.color = 'B';
w.color = 'R';
rightRotate(w);
w = x.father.right;
}else {
//兄弟节点的右孩子是红色
w.color = x.father.color;
x.father.color = 'B';
w.right.color = 'B';
if(w == x.father.right)
leftRotate(x.father);
else
rightRotate(x.father);
x = root;
}
}
}
}
}
x.color = 'B';
}
5. 其他
红黑树的扩张:设f是n 个结点的红黑树T扩张的属性,且假设对任一结点,f的值仅依赖于结点x、x.left和x.right 的信息,还可能包括x.left.f和x..right.f。那么,我们可以在插入和删除操作期间对 T的所有结点的f值进行维护,并且不影响这两个操作的O(lgn)渐近时间性能。
6. 完整代码
package s22;
public class RedBlackTree {
//节点类,包含颜色、父节点、左孩子、右孩子、值
class Node{
char color;//R:red,B:black
Node father =null;
Node left = null;
Node right = null;
int value;
private Node(){}
private Node(int value){this.value = value;}
}
private Node root;
private int size;
private Node nil; //叶子节点
public RedBlackTree(){
size = 0;
nil = new Node();
nil.color = 'B';
root = nil;
}
public void add(int value){
Node newNode = new Node(value);//创建新节点
Node y = nil;
Node x = root;
while(x!=nil){ //递归查找插入位置
y=x;
if(newNode.value < x.value)
x=x.left;
else
x=x.right;
}
newNode.father = y;
if(y==nil) { //第一个节点,更新根节点
root = newNode;
}
else if(newNode.value < y.value) //判断新增节点作为左孩子还是右孩子
y.left = newNode;
else
y.right = newNode;
newNode.left = nil;
newNode.right = nil;
newNode.color = 'R'; //默认红色
rbInsertFixup(newNode); //更新颜色
}
/*规则修复
1.当新增节点的父节点是黑色,则无需调整
2.新增节点为根节点,则改为黑色
3.父节点为红色
a.叔节点为红色,叔、父都调为黑色,爷爷调为红色
b.叔节点为黑色,当前节点为右孩子,和父节点进行左旋
c.叔节点为黑色,当前节点为左孩子,父节点调为黑色,爷节点调为红色,右旋
*/
private void rbInsertFixup(Node node){
while(node.father.color == 'R'){
Node y = null;
//获取叔节点
if(node.father==node.father.father.left)
y = node.father.father.right;
else
y = node.father.father.left;
if(y!=null) {
if (y.color == 'R') {
node.father.color = 'B';
y.color = 'B';
node.father.father.color = 'R';
node = node.father.father; //取爷爷节点向上迭代
} else if (node == node.father.right) {
node = node.father;
leftRotate(node);
} else {
node.father.color = 'B';
node.father.father.color = 'R';
rightRotate(node);
}
}
}
root.color = 'B';
}
/*左旋
将右孩子y旋转到当前节点x,当前节点x及其左子树作为y的左子树
将y的左子树移为x的右子树
*/
public void leftRotate(Node x){
Node y = x.right;
if(y!=nil){
//y的左子树移为x的右子树
x.right = y.left;
if(y.left!=nil)
y.left.father = x;
//y顶替x的位置
y.father = x.father;
if(x.father==nil)
root = y;
else if(x==x.father.left)
x.father.left = y;
else
x.father.right = y;
//x作为y的左子树
y.left = x;
x.father = y;
}
}
/*右旋
将左孩子y旋转到当前节点x,当前节点x及其右子树作为y的右子树
将y的右子树移为x的左子树
*/
public void rightRotate(Node x){
Node y = x.left;
if(y!=nil){
//y的右子树移为x的左子树
x.left = y.right;
if(y.right!=nil)
y.right.father = x;
//y顶替x的位置
y.father = x.father;
if(x.father==nil)
root = y;
else if(x.father.left == x)
x.father.left = y;
else
x.father.right = y;
//x作为y的右子树
y.right = x;
x.father = y;
}
}
/*节点上移
移除节点u,节点v上移
*/
private void transplant(Node u, Node v){
if(u.father == nil)
root = v;
else if(u==u.father.left)
u.father.left = v;
else
u.father.right = v;
if(v!=nil)
v.father = u.father;
}
public void delete(Node z){
Node x;
Node y = z;
char originalColor = y.color;
if(z.left==nil) { //无左孩子(包含无孩子),右孩子上移
x=z.right;
transplant(z, z.right);
}
else if(z.right==nil) { //无有孩子
x=z.left;
transplant(z, z.left); //左孩子上移
}
else{//有两个孩子
//取右孩子树的最小值,最小值没有左孩子,将最小节点提到原先z的位置
y = minimum(z.right);
originalColor = y.color; //最小节点上提,其右子树可能破坏规则
x=y.right;
if(y.father!=z){
transplant(y,y.right);// 最小节点提取出后右孩子树上移
y.right = z.right; //y成为右子树中第一个节点,且没有左孩子
y.right.father = y;
}else {
x.father = y;
}
transplant(z,y); //移除z,y上移
y.left = z.left; //原z的左孩子,变为y的左孩子
y.left.father = y;
y.color=z.color; //最小节点颜色与删除节点保持一致,此次未破坏规则
}
if(originalColor=='B') //只有原节点是黑色才破坏规则
rbDeleteFixup(x);
}
private void rbDeleteFixup(Node x){
while(x!=root && x.color == 'B'){
Node w = null;
//获取兄弟节点
if(x==x.father.left)
w = x.father.right;
else
w = x.father.left;
if(w!=null) {
if (w.color == 'R') {
w.color = 'B';
x.father.color = 'R';
if(w == x.father.right) {
leftRotate(x.father);
w = x.father.right;
}else {
rightRotate(x.father);
w = x.father.left;
}
} else {
if (w.left.color == 'B' && w.right.color == 'B') {
w.color = 'R';
x = x.father;
} else {
if (w.right.color == 'B') {
w.left.color = 'B';
w.color = 'R';
rightRotate(w);
w = x.father.right;
}else {
w.color = x.father.color;
x.father.color = 'B';
w.right.color = 'B';
if(w == x.father.right)
leftRotate(x.father);
else
rightRotate(x.father);
x = root;
}
}
}
}
}
x.color = 'B';
}
//根据key迭代查找节点
public Node iterativeTreeSearch(Node x, Integer key){
while(x!=null && key != x.value){
if(key < x.value)
x = x.left;
else
x = x.right;
}
return x;
}
//获取最小节点
public Node minimum(Node x){
while(x.left != null)
x = x.left;
return x;
}
//获取树的最大节点
public Node maximum(Node x){
while(x.right != null)
x = x.right;
return x;
}
//获取根节点
public Node getRoot() {
return root;
}
//获取大小
public Integer getSize() {
return size;
}
}