红黑树的性质
-
节点是红色或黑色
-
根是黑色
-
所有叶子都是黑色(叶子是NIL节点)
-
每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
-
从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
首先是红黑树的插入
插入首先和二叉树插入一样,只是新节点的颜色一定要设置成红色,还要新节点的父节点属性要指向父节点;
插入以后,此时该树不一定是平衡的,要根据父亲和叔叔节点的颜色进行判断,处理,使它达到平衡;
下图代表不同的情况对应不同的处理
其中主要是通过左旋和右旋来使树达到平衡;
以下是我对此进行的封装
// 左旋
private void leftTurn(Node upNode, Node downNode) {
Node dowm_left=downNode.leftChild;
if (upNode.parentNode != null) {
grandFather = upNode.parentNode;
if (grandFather.leftChild == upNode) {
grandFather.leftChild = downNode;
downNode.parentNode = grandFather;
}
if (grandFather.rightChild == upNode) {
grandFather.rightChild = downNode;
downNode.parentNode = grandFather;
}
} else {
downNode.parentNode = null;
root = downNode;
}
if(dowm_left!=null){
upNode.rightChild=dowm_left;
}
downNode.leftChild = upNode;
upNode.parentNode = downNode;
upNode.rightChild = null;
System.out.println("左旋");
}
// 右旋
private void rightTurn(Node upNode, Node dowmNode) {
Node down_right=dowmNode.rightChild;
dowmNode.rightChild = upNode;
if (upNode.parentNode != null) {// 当grandFather不是root节点时;
dowmNode.parentNode = upNode.parentNode;
if (upNode.parentNode.leftChild == upNode) {
upNode.parentNode.leftChild = dowmNode;
} else {
upNode.parentNode.rightChild = dowmNode;
}
} else {
dowmNode.parentNode = null;
this.root = dowmNode;
}
if(down_right!=null){
upNode.leftChild=down_right;
}
upNode.parentNode = dowmNode;
upNode.leftChild = null;
System.out.println("右旋");
}
然后是删除
删除比插入更复杂
首先共有三种情形
1.无子节点时,删除节点可能为红色或者黑色;
1.1 如果为红色,直接删除即可,不会影响黑色节点的数量;
1.2 如果为黑色,则需要进行删除平衡的操作了;
2.只有一个子节点时,删除节点只能是黑色,其子节点为红色,否则无法满足红 黑树的性质了。 此时用删除节点的子节点接到父节点,且将子节点颜色涂黑,保证黑色数量。
3.有两个子节点时,与二叉搜索树一样,使用后继节点作为替换的删除节点,情形转至为1或2处理。
其实情形3和情形2 最终都要转换成情形1
情形3和2 都要找到一个替代节点,即数值大小最接近于要删除的节点,这个替代节点最终是一个叶子节点,变成了情形1;所以主要的操作就是针对情形1;
如果删除节点是红色叶子节点,则直接删除;以下主要是针对黑色叶子节点;
下面是找替代节点的代码
如果右节点不为null,后继节点就是右节点的最左节点
private Node findStepNode(Node deleteNode) {
if (deleteNode.rightChild != null) {
Node x = deleteNode.rightChild.leftChild;
if (x != null) {
while (x.leftChild != null) {
x = x.leftChild;
}
return x;
} else
return deleteNode.rightChild;
}
else {
Node x = deleteNode.leftChild.rightChild;
if (x != null) {
while (x.rightChild != null) {
x = x.rightChild;
}
return x;
} else
return deleteNode.rightChild;
}
}
```![在这里插入图片描述](https://img-blog.csdnimg.cn/2020010521224947.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxODAyMjI5,size_16,color_FFFFFF,t_70)
该图是针对情形1的讨论
其中兄子节点为黑代表子节点不存在;
以下是完整代码
package 红黑树;
public class 红黑树 {
Node root = null;
Node currentNode = null;
Node grandFather = null;
public static void main(String[] args) {
红黑树 tree = new 红黑树();
//
//
// tree.insert(3);
// tree.insert(2);
tree.insert(1);
tree.insert(2); tree.insert(3);tree.insert(4);tree.insert(5);tree.insert(6);
tree.delete(tree.root.rightChild);
//System.out.println(tree.root.value);
// tree.insert(7);tree.insert(8);tree.insert(5);;tree.insert(4);
// System.out.println(tree.root.value + "_" + tree.root.leftChild.value
// + "_" + tree.root.rightChild.value);
System.out.println(tree.root.rightChild.leftChild.value+tree.root.rightChild.leftChild.color+"+");
System.out.println(tree.root.rightChild.rightChild.value+tree.root.rightChild.rightChild.color+"+");
// System.out.println(tree.root.rightChild.rightChild.leftChild.value+tree.root.rightChild.rightChild.leftChild.color+"+");
// System.out.println(tree.root.rightChild.rightChild.rightChild.value+tree.root.rightChild.rightChild.rightChild.color);
}
// 删除节点
public void delete(Node deleteNode) {
//System.out.println("s"+deleteNode.value);
// 一些不需要特别处理的
/*
* 1.deleteNode为红色节点 2.当只有一个子节点时,deleteNode的颜色是黑色,子节点的颜色是红色;
* 否则就要用后继节点代替:最接近目标节点的; 3.后继节点为删除节点的右子节点的最左节点,或者左节点的最右节点;
* 然后与目标节点互换位置,即变成了删除叶子节点的情形
*
* 情形1:无子节点时,删除节点可能为红色或者黑色; 1.1 如果为红色,直接删除即可,不会影响黑色节点的数量; 1.2
* 如果为黑色,则需要进行删除平衡的操作了; 情形2: 只有一个子节点时,删除节点只能是黑色,其子节点为红色,否则无法满足红黑树的性质了。
* 此时用删除节点的子节点接到父节点,且将子节点颜色涂黑,保证黑色数量。
* 情形3:有两个子节点时,与二叉搜索树一样,使用后继节点作为替换的删除节点,情形转至为1或2处理。
*/
Node parNode = deleteNode.parentNode;
// 情形3 存在两个子节点
if (deleteNode.leftChild != null && deleteNode.rightChild != null) {
// 交换两个节点位置;只是交换数值,不改变颜色;此时还是处于平衡;
Node stepNode = findStepNode(deleteNode);
exchangeNode(deleteNode, stepNode);
delete(stepNode);
//此时 stepNode为要删除的点 deleteNode已成为后继节点 不一定是叶子节点 可能有一个 可能没有子节点 转到情形1或2
}
// 情形1 无子节点
else if (deleteNode.leftChild == null && deleteNode.rightChild == null) {
if (deleteNode.color == "red") {
if (deleteNode.parentNode.leftChild == deleteNode) {
deleteNode.parentNode.leftChild = null;
} else
deleteNode.parentNode.rightChild = null;
}
// 删除节点为黑色,则必存在兄弟节点
else {
Node broNode = null;
System.out.println("ar="+parNode.leftChild.value+"_"+deleteNode.value);
// 兄在右
if (parNode.leftChild == deleteNode) {
broNode = parNode.rightChild;
Node bro_left = broNode.leftChild;
Node bro_right = broNode.rightChild;
if (broNode.color == "black") {
// 当兄无子节点时
if (bro_left == null && bro_right == null) {
if (parNode.color == "red") {
parNode.color = "black";
parNode.leftChild=null;
} else {
// 兄变红
parNode.leftChild = null;
broNode.color = "red";
}
// 此时以达到平衡
}
// 当兄有子节点时 且子全红或右红左空
else if (bro_right.color == "red") {
// 父节点左旋,父 兄交换颜色 右子节点变黑
right_red(parNode, broNode, bro_right, bro_left);
// 此时以达到平衡
}
// 左子节点为红,右为空时
else if (bro_left.color == "red" && bro_right == null) {
// 兄右旋,交换颜色 跳到上个if 右子节点为红
rightTurn(broNode, bro_left);
bro_left.color = "black";
broNode.color = "red";
right_red(parNode, bro_left, bro_right, broNode);
}
}
//当兄为红色
else{
//兄红 兄在父右边
leftTurn(parNode, broNode);
broNode.color="black";
parNode.color = "black";
bro_left.color="red";
parNode.leftChild=null;
}
}
//兄在左边
else {
broNode = parNode.leftChild;
Node bro_left = broNode.leftChild;
Node bro_right = broNode.rightChild;
if (broNode.color == "black") {
// 当兄无子节点时
if (bro_left == null && bro_right == null) {
if (parNode.color == "red") {
parNode.color = "black";
parNode.leftChild=null;
} else {
// 兄变红
parNode.leftChild = null;
broNode.color = "red";
}
// 此时以达到平衡
}
// 当兄有子节点时 且子全红或左红右空
else if (bro_right.color == "red") {
// 父节点右旋,父 兄交换颜色 左子节点变黑
right_red2(parNode, broNode, bro_right, bro_left);
// 此时以达到平衡
}
// 右子节点为红,左为空时
else if (bro_right.color == "red" && bro_left == null) {
// 兄左旋,交换颜色 跳到上个if 右子节点为红
leftTurn(broNode, bro_right);
bro_right.color = "black";
broNode.color = "red";
right_red2(parNode, bro_right, null, broNode);
}
}
//当兄为红色
else{
//兄红 兄在父左边
rightTurn(parNode, broNode);
broNode.color="black";
parNode.color = "black";
bro_right.color="red";
parNode.rightChild=null;
}
}
}
}
// 情形2 有一个子节点 若 当前为黑,子为红,交换颜色即可,否则 寻找后继节点,直到后继结点无子节点 转到情形1
else {
// deleteNode一定为黑色
Node left = deleteNode.leftChild;
Node right = deleteNode.rightChild;
if (left != null && left.color == "red") {
exchangeNode(deleteNode, left);
deleteNode.color = "black";
deleteNode.leftChild=null;
} else if (right != null && right.color == "red") {
exchangeNode(deleteNode, right);
deleteNode.color = "black";
deleteNode.rightChild=null;
}
// 子为黑色时,寻找后继节点,递归
else {
delete(deleteNode);
}
}
}
private void right_red2(Node parNode, Node broNode, Node bro_right, Node bro_left) {
rightTurn(parNode, broNode);
broNode.color = "red";
parNode.color = "black";
bro_left.color = "black";
// 左右节点全为红时
if (bro_right != null && bro_right.color == "red") {
parNode.leftChild = bro_right;
bro_right.parentNode = parNode;
}
parNode.rightChild = null;
}
private void right_red(Node parNode, Node broNode, Node bro_right, Node bro_left) {
leftTurn(parNode, broNode);
broNode.color = "red";
parNode.color = "black";
bro_right.color = "black";
// 左右节点全为红时
if (bro_left != null && bro_left.color == "red") {
parNode.rightChild = bro_left;
bro_left.parentNode = parNode;
}
parNode.leftChild = null;
}
private void exchangeNode(Node deleteNode, Node stepNode) {
int t = stepNode.value;
stepNode.value = deleteNode.value;
deleteNode.value = t;
}
private Node findStepNode(Node deleteNode) {
if (deleteNode.rightChild != null) {
Node x = deleteNode.rightChild.leftChild;
if (x != null) {
while (x.leftChild != null) {
x = x.leftChild;
}
return x;
} else
return deleteNode.rightChild;
}
else {
Node x = deleteNode.leftChild.rightChild;
if (x != null) {
while (x.rightChild != null) {
x = x.rightChild;
}
return x;
} else
return deleteNode.rightChild;
}
}
// 新增节点
public void insert(Integer value) {
Node newNode = new Node();
newNode.color = "red";
newNode.value = value;
if (root == null) {
root = newNode;
root.color = "black";
currentNode = root;
} else {
addNewNode(newNode, this.root);
currentNode = newNode.parentNode;
adjust(newNode);
}
}
private void adjust(Node newNode) {
// 当父节点为黑色时,不需另外处理;
if (currentNode != null && currentNode.color == "black") {
//System.out.println("当父节点为黑色时,不需另外处理;");
currentNode = newNode;
}
// 当父节点为红色;
else {
//System.out.println("当父节点为红色时");
Node granNode1 = currentNode.parentNode;
//System.out.println("ff=" + granNode1.value);
Node uncleNode = null;
if (granNode1.leftChild == currentNode && granNode1.rightChild != null)
uncleNode = granNode1.rightChild;
else if (granNode1.rightChild == currentNode && granNode1.leftChild != null)
uncleNode = granNode1.leftChild;
// 当父节点为红色,叔叔节点也为红色时;
if (uncleNode != null && uncleNode.color == "red") {
//System.out.println("uncle=" + uncleNode.color);
// 当grandFather为root时,父和叔都变黑 祖不变
//System.out.println("祖父节点的父节点为null时");
if (granNode1.parentNode == null) {
currentNode.color = "black";
uncleNode.color = "black";
}
// 不为root时,父和叔都变黑,祖变红;
else {
// System.out.println("祖父节点不为root");
granNode1.color = "red";
uncleNode.color = "black";
currentNode.color = "black";
newNode = granNode1;
adjust(newNode);
}
}
// 当父节点为红色,叔叔节点为黑色,或者为空时;
else {
//System.out.println("当父节点为红色,叔叔节点为黑色,或者为空时");
// 当父节点在祖父节点的左边left
if (granNode1.leftChild == currentNode) {
// 新节点在父节点右边 right时 新节点与父节点左旋,变色 父节点与祖父节点 右旋
if (currentNode.rightChild != null && newNode.value == currentNode.rightChild.value) {
leftTurn(currentNode, newNode);
// newNode与currentNode的位置已换;
granNode1.color = "red";
newNode.color = "black";
//System.out.println("pop=" + currentNode.color + "_" + newNode.color);
rightTurn(granNode1, newNode);
}
// 当 新节点在父节点左边时,变色(父变黑,祖变红),再父节点与祖父节点 右旋;
else {
granNode1.color = "red";
currentNode.color = "black";
rightTurn(granNode1, currentNode);
}
}
// 当父节点在祖父节点的右边right
else {
// 当 新节点在父节点左边时,新节点与父节点右旋,变色 父节点与祖父节点 左旋
if (currentNode.leftChild != null && newNode.value == currentNode.leftChild.value) {
rightTurn(currentNode, newNode);
currentNode.color = "black";
newNode.color = "red";
leftTurn(granNode1, currentNode);
}
// 新节点在父节点右边 right时;变色(父变黑,祖变红),再父节点与祖父节点 左旋
else {
currentNode.color = "black";
granNode1.color = "red";
leftTurn(granNode1, currentNode);
}
}
}
}
}
// 根据newNode的大小,将新节点置于当前节点的左边或者右边;
private void addNewNode(Node newNode, Node node) {
if (newNode.value <= node.value) {
if (node.leftChild != null) {
addNewNode(newNode, node.leftChild);
//System.out.println("gggggggggggg");
} else {
node.leftChild = newNode;
newNode.parentNode = node;
System.out.println(newNode.value + "加在" + node.value + "左边");
}
} else {
if (node.rightChild != null) {
addNewNode(newNode, node.rightChild);
//System.out.println("gggggggggggg");
} else {
node.rightChild = newNode;
newNode.parentNode = node;
System.out.println(newNode.value + "加在" + node.value + "右边");
}
}
}
// 左旋
private void leftTurn(Node upNode, Node downNode) {
Node dowm_left=downNode.leftChild;
if (upNode.parentNode != null) {
grandFather = upNode.parentNode;
if (grandFather.leftChild == upNode) {
grandFather.leftChild = downNode;
downNode.parentNode = grandFather;
}
if (grandFather.rightChild == upNode) {
grandFather.rightChild = downNode;
downNode.parentNode = grandFather;
}
} else {
downNode.parentNode = null;
root = downNode;
}
if(dowm_left!=null){
upNode.rightChild=dowm_left;
}
downNode.leftChild = upNode;
upNode.parentNode = downNode;
upNode.rightChild = null;
System.out.println("左旋");
}
// 右旋
private void rightTurn(Node upNode, Node dowmNode) {
Node down_right=dowmNode.rightChild;
dowmNode.rightChild = upNode;
if (upNode.parentNode != null) {// 当grandFather不是root节点时;
dowmNode.parentNode = upNode.parentNode;
if (upNode.parentNode.leftChild == upNode) {
upNode.parentNode.leftChild = dowmNode;
} else {
upNode.parentNode.rightChild = dowmNode;
}
} else {
dowmNode.parentNode = null;
this.root = dowmNode;
}
if(down_right!=null){
upNode.leftChild=down_right;
}
upNode.parentNode = dowmNode;
upNode.leftChild = null;
System.out.println("右旋");
}
class Node {
Node parentNode;
Node leftChild;
Node rightChild;
Integer value;
String color;
}
}