看此篇文章之前需要先了解二叉平衡树和二叉搜索树,本篇文章不在此赘述。该文章的内容主要是红黑树的插入和删除操作。
红黑树的基本性质
1.每个节点只能是红色或者黑色。
2.根节点必须是黑色。
3.红色的节点,它的叶节点只能是黑色。
4.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
下图是一个红黑树的例子:
红黑树内涉及的节点类代码如下:
public class Node插入操作实现。implements Serializable,Comparable > { private int color; //用来标记节点颜色 private K key; private V value; private Node pro; //parent private Node left; //left child private Node right; //right child public Node(int color, K key, V value, Node pro, Node left, Node right) { this.color = color; this.key = key; this.value = value; this.pro = pro; this.left = left; this.right = right; } public int getColor() { return color; } public void setColor(int color) { this.color = color; } 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 Node getPro() { return pro; } public void setPro(Node pro) { this.pro = pro; } public Node getLeft() { return left; } public void setLeft(Node left) { this.left = left; } public Node getRight() { return right; } public void setRight(Node right) { this.right = right; } public Node() { } //此处主要是重写了equals方法,为了方便接下来的使用 @Override public boolean equals(Object obj) { if(this==obj) return true; if(obj==null || this.getClass()!=obj.getClass()) return false; Node,?> node=(Node, ?>) obj; return Objects.equals(key,node.key); } @Override public int hashCode() { return Objects.hash(key); } //插入数值时比较大小。 @Override public int compareTo(Node o) { return this.hashCode()-o.hashCode(); } }
先来说说为什么要插入的是红色节点。插入黑色节点会破坏性质4.
我们先来说说一般二叉搜索树的插入操作。
public void put(K key, V value) {
Node z = new Node<>();
z.setKey(key);
z.setValue(value);
z.setColor(RED);
this.size++;
Node x = root;
Node y = null;
while (x != null) { // find a position to insert
y = x;
int sp = z.compareTo(x);
if (sp < 0) {
x = x.getLeft();
} else if (sp > 0) {
x = x.getRight();
} else {
x.setValue(value); //要插入的节点存在,则更新其值,不再进行操作。
this.size--;
return;
}
z.setPro(y);
}
if (y == null) { //要插入的节点是头结点
root = z;
} else if (z.compareTo(y) < 0) {
y.setLeft(z); // insert into left
} else if (z.compareTo(y) > 0) {
y.setRight(z); //insert into right
}
//插入完成后,有可能破坏红黑树的平衡性(即出现连续的红节点)
//需要动态的调整
this.fixup(z);
}
插入操作的情况有:
1.插入的节点是头结点,置黑,结束。
2.插入的节点存在,更新节点的值,结束。
3.插入的节点的父节点为黑节点,不影响结束。
4.插入的节点的父节点为红色,需要自平衡操作。(这里插入祖父节的左子树为例,右子树是一样的)
4.1.叔叔结点存在并且为红结点
(并且一定为红色节点,若为黑色节点,则父节点和叔节点不平衡,破坏了性质5)
处理操作如图,并将pp作为插入节点继续向上平衡。
4.2 叔叔节点不存在,插入结点是其父结点的左子结点
如图操作:
右旋操作:
4.2 叔叔节点不存在,插入结点是其父结点的右子结点
操作为:
左旋操作为:
另外当插入到服务节点的右子树时和上述操作是一样的,这里不在赘述。
调整操作
private void fixup(Nodez) { while (z.getPro() != null && z.getPro().getColor() == RED) { //parent exist and color is red if (z.getPro().getPro() != null) { if (z.getPro().equals(z.getPro().getPro().getLeft())) { //parent is left node Node y = z.getPro().getPro().getRight(); // uncle node if (y != null && y.getColor() == RED) { //uncle node exist and color is red z.getPro().setColor(BLACK); //set the color of parent and uncle are black y.setColor(BLACK); z.getPro().getPro().setColor(RED); //set the color of grandpa is red z = z.getPro().getPro(); //current insert node is grandpa } else { //uncle node not exist or color is black if (z.equals(z.getPro().getRight())) { //z is right node z = z.getPro(); //1 //this z (1) is different from next z (2) this.leftRotate(z); } z.getPro().setColor(BLACK);//2 // z is left node z.getPro().getPro().setColor(RED); this.rightRotate(z.getPro().getPro()); } } else if (z.getPro().equals(z.getPro().getPro().getRight())) { //parent is right node Node y = z.getPro().getPro().getLeft(); if (y != null && y.getColor() == RED) { z.getPro().setColor(BLACK); y.setColor(BLACK); z.getPro().getPro().setColor(RED); z = z.getPro().getPro(); } else { if (z.equals(z.getPro().getLeft())) { z = z.getPro(); this.rightRotate(z); } z.getPro().setColor(BLACK); z.getPro().getPro().setColor(RED); this.leftRotate(z.getPro().getPro()); } } } } this.root.setColor(BLACK); //插入的节点为头结点,不要忘记了把颜色设置为黑色 }
左旋操作
private void leftRotate(Nodex) { Node y = x.getRight(); x.setRight(y.getLeft()); if (y.getLeft() != null) { y.getLeft().setPro(x); } y.setPro(x.getPro()); if (x.getPro() == null) { this.root = y; } else if (x.equals(x.getPro().getLeft())) { x.getPro().setLeft(y); } else { x.getPro().setRight(y); } y.setLeft(x); x.setPro(y); }
右旋操作
private void rightRotate(Nodex) { Node y = x.getLeft(); x.setLeft(y.getRight()); if (y.getRight() != null) { y.getRight().setPro(x); } y.setPro(x.getPro()); if (x.getPro() == null) { this.root = y; } else if (x.equals(x.getPro().getLeft())) { x.getPro().setLeft(y); } else { x.getPro().setRight(y); } y.setRight(x); x.setPro(y); }
测试结果(请读者自己根据二叉树的先序遍历和中序遍历重建二叉树,进而验证结果):
public static void main(String[] args) {
RBTree tree = new RBTree<>();
tree.put("1", 333);
tree.put("12", 3331);
tree.put("41", 32);
tree.put("21", 35);
tree.put("4", 33343);
tree.put("33", 3353);
tree.put("38", 3353);
tree.put("6", 3353);
tree.put("52", 3353);
tree.put("46", 3353);
tree.inorder(tree.getRoot());
tree.preOrder(tree.getRoot());
System.out.println(tree.getDepth(tree.getRoot().getLeft())-tree.getDepth(tree.getRoot().getRight()));
}
输出:
***InOrder**** Key:1 Value:333 Key:4 Value:33343 Key:6 Value:3353 Key:12 Value:3331 Key:21 Value:35 Key:33 Value:3353 Key:38 Value:3353 Key:41 Value:32 Key:46 Value:3353 Key:52 Value:3353 ***preOrder**** Key:33 Color: 0 Key:12 Color: 1 Key:4 Color: 0 Key:1 Color: 1 Key:6 Color: 1 Key:21 Color: 0 Key:41 Color: 1 Key:38 Color: 0 Key:52 Color: 0 Key:46 Color: 1
下篇文章将会实现红黑树的删除操作。
链接: 图片来源



