手写红黑树【数据结构】
- 前言
- 版权
- 推荐
- 手写红黑树
- 一、理论知识
- 红黑树的特征
- 增加
- 删除
- 二、手写代码
- 初始-树结点
- 初始-红黑树
- 初始-遍历
- 初始-判断红黑树是否有效
- 查找
- 增加-1.父为黑,直接插入
- 增加-2. 父叔为红,颜色调换
- 增加-3. 父红叔黑,颜色调换,再移动
- 增加-4. 父子不同向,先掰直,再执行第3种情况
- 测试增加
- 删除-0 初始化数据
- 删除-1.单个红节点 直接删除
- 删除-3.带有一个子节点
- 删除-4.带有两个子节点
- 删除-2.单个黑结点
- 2.1.1兄黑,对侄红
- 2.1.2兄黑,顺侄红
- 2.1.3 兄黑,双侄黑
- 删除-2.单个黑结点 2.2兄红
- 测试删除
- 附录源码
- 最后
前言
2024-3-30 10:52:57
昨天晚上B站看到的视频
00:00~01:00
以下内容源自《【数据结构】》
仅供学习交流使用
版权
禁止其他平台发布时删除以下此话
本文首次发布于CSDN平台
作者是CSDN@日星月云
博客主页是https://jsss-1.blog.csdn.net
禁止其他平台发布时删除以上此话
推荐
我红黑树那么牛,你们凭什么不用我?
手写红黑树
一、理论知识
红黑树的特征
红黑树是一种二叉平衡树,只不过这个平衡不是那么严苛,只需黑平衡就行
- 结点分为两种颜色
- 根节点是黑色
- 叶子结点是黑色的,叶子结点是不存储数据的空结点
- 两个红结点不能相连,即红父亲的孩子都是黑色的
- 对于任意一个结点,其到叶子结点的路径上的黑色结点数量是一致的
增加
视频
插入结点的颜色是红色的
因为是黑平衡,所以插入红结点有概率不会破坏这个规则
- 父为黑,直接插入
- 父叔为红,颜色调换
- 父红叔黑,颜色调换,再移动
- 父子不同向,先掰直,再执行第3种情况
删除
视频
https://www.processon.com/view/link/6550422f54fca5688e143664
二、手写代码
为了实现简单,加入parent的指针,和isLeaf的标记
可以使用HashMap用来记录每一个叶子结点的父亲结点,即键是叶子,值是父亲;
也可以直接在Node结点中加入双亲指针。
根节点的父亲结点是null
特别注意的是,parent的维护
如果是叶子结点,它的isLeaf的值为true。
初始-树结点
//结点
class TreeNode {
//true是黑色,false是红色
boolean color;
//数据
Integer data;
TreeNode left;
TreeNode right;
private static final boolean RED = false;
private static final boolean BLACK = true;
//是否是叶子结点
boolean isLeaf;
//方便实现
TreeNode parent;
public TreeNode() {
}
public TreeNode(int data) {
this.data = data;
}
public TreeNode(boolean color, Integer data) {
this.color = color;
this.data = data;
}
public TreeNode(boolean color, Integer data, TreeNode parent) {
this.color = color;
this.data = data;
this.parent = parent;
}
public TreeNode(boolean color, Integer data, TreeNode parent, boolean isLeaf) {
this.color = color;
this.data = data;
this.parent = parent;
this.isLeaf = isLeaf;
}
// public TreeNode(Integer data,TreeNode left, TreeNode right) {
// this.data = data;
// this.left = left;
// this.right = right;
// }
public boolean isBlack() {
return color == BLACK;
}
@Override
public String toString() {
return "TreeNode{" +
"color=" + color +
", data=" + data +
'}';
}
}
初始-红黑树
package test;
import java.util.LinkedList;
import java.util.Queue;
public class RedBlackTree {
private static final boolean RED = false;
private static final boolean BLACK = true;
//根结点
TreeNode root;
public void initRoot(int data) {
root = new TreeNode(BLACK, data, null, false);
TreeNode nil = new TreeNode(BLACK, null, root, true);
root.left = nil;
root.right = nil;
}
/**
* 增加
* 1. 父为黑,直接插入
* 2. 父叔为红,颜色调换
* 3. 父红叔黑,颜色调换,再移动
* 4. 父子不同向,先掰直,再执行第3种情况
*
* @param data 数据
*/
public void add(int data) {
}
/**
* 删除
* 1.单个红节点 直接删除
* 2.单个黑节点
* 2.1兄黑
* 2.1.1 对侄红 (指方向相反的侄节点)
* 父兄交替旋转、然后按父红兄弟黑换色 (最后一步的换色,父红两兄弟黑,是按交替旋转之后的关系处理。)
* 2.1.2 顺侄红(指方向相同的侄节点)
* 兄侄交替旋转,并调换颜色,就会变成对侄红,然后按2.1.1处理
* 2.1.3 双侄黑
* 兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
*
* 2.2 兄红
* 父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
* 3.带有一个子节点(当一个节点只有一个子节点时(空叶子除外),该节点必定是黑节点,其子节点必定是红色)
* 用红子节点值替换,然后直接删除红子节点
* 4.带有两个子节点!
* 找到左子树中最靠右的子节点,用该节点值替换,并删除该节点按情况1,2,3处理(左子树中最大的值,也是离其最近的值)
*
* @param data 数据
*/
public void delete(int data) {
}
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
tree.inorder(tree.root);
// tree.levelOrder(tree.root);
}
}
初始-遍历
//中序遍历
public void inorder(TreeNode root) {
if (root == null) {
return;
}
if (!root.left.isLeaf) {
inorder(root.left);
}
System.out.println(root);
if (!root.right.isLeaf) {
inorder(root.right);
}
}
//层序遍历
public void levelOrder(TreeNode root) {
if (root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode cur = queue.poll();
System.out.print(cur + "\t");
if (cur.left != null) {
queue.add(cur.left);
}
if (cur.right != null) {
queue.add(cur.right);
}
}
System.out.println();
}
}
初始-判断红黑树是否有效
//判断是否是有效的红黑树
public static boolean isValidRedBlackTree(TreeNode root) {
if (root == null) {
return true;
}
// 检查根节点是否是黑色
if (root.color != BLACK) {
return false;
}
// 计算黑高度,并检查红黑平衡
blackHeight = -1;
if (!checkBlackBalance(root, 0)) {
return false;
}
// 递归检查每个节点
return isValidRedBlackSubtree(root);
}
private static boolean checkBlackBalance(TreeNode node, int currentBlackHeight) {
if (node.isLeaf) {
if (blackHeight == -1) {
blackHeight = currentBlackHeight;
return true;
} else {
return currentBlackHeight == blackHeight;
}
}
if (node.color == BLACK) {
currentBlackHeight++;
}
return checkBlackBalance(node.left, currentBlackHeight) && checkBlackBalance(node.right, currentBlackHeight);
}
private static boolean isValidRedBlackSubtree(TreeNode node) {
if (node == null) {
return true;
}
// 检查红黑树性质
if (node.color == RED) {
if ((node.left != null && node.left.color != BLACK) || (node.right != null && node.right.color != BLACK)) {
return false;
}
}
// 递归检查左右子树
return isValidRedBlackSubtree(node.left) && isValidRedBlackSubtree(node.right);
}
查找
/**
* 查找
*
* @param data 数据
* @return 查找结点,如果差不到就会返回叶子结点
*/
public TreeNode find(int data) {
TreeNode find = root;
while (!find.isLeaf) {
if (data < find.data) {
find = find.left;
} else if(data==find.data){
return find;
} else if (data > find.data) {
find = find.right;
}
}
return find;
}
增加-1.父为黑,直接插入
/**
* 增加
* 1. 父为黑,直接插入
* 2. 父叔为红,颜色调换
* 3. 父红叔黑,颜色调换,再移动
* 4. 父子不同向,先掰直,再执行第3种情况
*
* @param data 数据
*/
public void add(int data) {
if (root == null) {
initRoot(data);
return;
}
TreeNode find = find(data);
if (!find.isLeaf) {
System.out.println("不能插入相同数据的结点");
return;
}
TreeNode parent = find.parent;
TreeNode newNode = new TreeNode(RED, data, parent);
TreeNode nil = new TreeNode(BLACK, null, newNode, true);
newNode.left = nil;
newNode.right = nil;
if (data < parent.data) {
parent.left = newNode;
} else {
parent.right = newNode;
}
//1.父为黑,直接插入
if (parent.isBlack()) {
//不需要额外的操作
} else {
//跳转2...
}
增加-2. 父叔为红,颜色调换
TreeNode grandpa = parent.parent;
TreeNode uncle = grandpa.left != parent ? grandpa.left : grandpa.right;
//2. 父叔为红,颜色调换
if (!uncle.isBlack()) {
parent.color = BLACK;
uncle.color = BLACK;
grandpa.color = RED;
//如果爷爷是根节点
if (grandpa == root) {
grandpa.color = BLACK;
return;
}
//爷爷变成红色后,它的父叔可能为红
TreeNode cur=grandpa;
parent=cur.parent;
grandpa=parent.parent;
if (parent==null||grandpa==null){
return;
}
uncle=grandpa.left != parent ? grandpa.left : grandpa.right;
while (!cur.isBlack()&&!parent.isBlack()&&!uncle.isBlack()){
parent.color = BLACK;
uncle.color = BLACK;
grandpa.color = RED;
//如果爷爷是根节点
if (grandpa == root) {
grandpa.color = BLACK;
break;
}
cur=grandpa;
parent=cur.parent;
grandpa=parent.parent;
uncle=grandpa.left != parent ? grandpa.left : grandpa.right;
}
} else {
//跳转3...
}
增加-3. 父红叔黑,颜色调换,再移动
//跳转3...
boolean right1 = data > parent.data;
boolean right2 = parent.data > grandpa.data;
boolean direct1 = right1 && right2;
boolean left1 = data < parent.data;
boolean left2 = parent.data < grandpa.data;
boolean direct2 = left1 && left2;
//3. 父红叔黑,颜色调换,再移动
if (direct1 || direct2) {
op(data, parent, grandpa);
} else {
//跳转4...
}
public void op(int data, TreeNode parent, TreeNode grandpa) {
boolean right1 = data > parent.data;
boolean right2 = parent.data > grandpa.data;
boolean direct1 = right1 && right2;
boolean left1 = data < parent.data;
boolean left2 = parent.data < grandpa.data;
boolean direct2 = left1 && left2;
boolean tmp = grandpa.color;
grandpa.color = parent.color;
parent.color = tmp;
TreeNode grandpaPa = grandpa.parent;
if (parent.data < grandpaPa.data) {
grandpaPa.left = parent;
} else {
grandpaPa.right = parent;
}
parent.parent = grandpaPa;
if (direct1) {
parent.left = grandpa;
grandpa.parent = parent;
} else if (direct2) {
parent.right = grandpa;
grandpa.parent = parent;
}
grandpa.left = new TreeNode(BLACK, null, grandpa, true);
grandpa.right = new TreeNode(BLACK, null, grandpa, true);
}
增加-4. 父子不同向,先掰直,再执行第3种情况
//跳转4...
//4. 父子不同向,先掰直,再执行第3种情况
if (left1) {
grandpa.right = newNode;
newNode.right = parent;
}
if (right1) {
grandpa.left = newNode;
newNode.left = parent;
}
newNode.parent = grandpa;
parent.parent = newNode;
TreeNode newNil = new TreeNode(BLACK, null, parent, true);
parent.left = newNil;
parent.right = newNil;
op(parent.data, newNode, grandpa);
测试增加
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
testAdd(tree);
}
private static void testAdd(RedBlackTree tree) {
tree.add(157);//0
tree.add(12);//1
tree.add(200);//1
tree.add(250);//2
tree.add(260);//3
tree.add(220);//2
tree.add(210);//4
tree.add(11);//1
tree.add(10);//3
tree.add(7);//2
tree.add(9);//4
tree.inorder(tree.root);
// tree.levelOrder(tree.root);
System.out.println(isValidRedBlackTree(tree.root));
}
11、10、7、9的插入图如下
2024-3-30 15:35:56
删除-0 初始化数据
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
// testAdd(tree);
initData(tree);
}
private static void initData(RedBlackTree tree) {
int[] nums={430,261,636,
95,344,559,822,
37,131,330,369,499,594,705,981,
262,345,485,664,818};
for (int i = 0; i < nums.length; i++) {
tree.add(nums[i]);
}
// tree.inorder(tree.root);
tree.levelOrder(tree.root);
System.out.println(isValidRedBlackTree(tree.root));
}
删除-1.单个红节点 直接删除
/**
* 删除
* 1.单个红节点 直接删除
* 2.单个黑节点
* 2.1兄黑
* 2.1.1 对侄红 (指方向相反的侄节点)
* 父兄交替旋转、然后按父红兄弟黑换色 (最后一步的换色,父红两兄弟黑,是按交替旋转之后的关系处理。)
* 2.1.2 顺侄红(指方向相同的侄节点)
* 兄侄交替旋转,并调换颜色,就会变成对侄红,然后按2.1.1处理
* 2.1.3 双侄黑
* 兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
*
* 2.2 兄红
* 父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
* 3.带有一个子节点(当一个节点只有一个子节点时(空叶子除外),该节点必定是黑节点,其子节点必定是红色)
* 用红子节点值替换,然后直接删除红子节点
* 4.带有两个子节点
* 找到左子树中最靠右的子节点,用该节点值替换,并删除该节点按情况1,2,3处理(左子树中最大的值,也是离其最近的值)
*
* @param data 数据
*/
public void delete(int data) {
TreeNode find = find(data);
if (find.isLeaf){
System.out.println("没有找到");
return;
}
//1.单个红节点
if (!find.isBlack()){
if (find.left.isLeaf&&find.right.isLeaf){
TreeNode parent = find.parent;
TreeNode nil=new TreeNode(BLACK,null,parent,true);
if (data<parent.data){
parent.left=nil;
}else {
parent.right=nil;
}
}else {
//4.带有两个子节点
}
}else {
//3.带有一个子节点,用红子节点值替换,然后直接删除红子节点
}
}
删除-3.带有一个子节点
//3.带有一个子节点,用红子节点值替换,然后直接删除红子节点
if (find.left.isLeaf&&!find.right.isBlack()){
find.data=find.right.data;
find.right= new TreeNode(BLACK,null,find,true);
}else if (find.right.isLeaf&&!find.left.isBlack()){
find.data=find.left.data;
find.left= new TreeNode(BLACK,null,find,true);
}
删除-4.带有两个子节点
else if (!find.left.isLeaf&&!find.right.isLeaf){//4.带有两个子节点
TreeNode replace = findReplace(find);
delete(replace.data);
find.data= replace.data;
}else {//2.单个黑结点
/**
* 查找替代的结点
* 中序遍历线索树的直接前驱结点
* @param node 删除的结点
* @return 查找替代
*/
public TreeNode findReplace(TreeNode node) {
TreeNode left = node.left;
while (!left.isLeaf) {
left=left.right;
}
return left.parent;
}
删除-2.单个黑结点
2.1.1兄黑,对侄红
TreeNode parent=find.parent;
TreeNode brother=parent.left!=find?parent.left:parent.right;
boolean left=find.data<parent.data;
//对侄
TreeNode duiNephew=left?brother.right:brother.left;
//顺侄
TreeNode shunNephew=left?brother.left:brother.right;
if (brother.isBlack()){//2.1兄黑
//2.1.1 对侄红
TreeNode duiNephew=left?brother.right:brother.left;
if (!duiNephew.isBlack()){
//父兄交替旋转
TreeNode grandpa=parent.parent;
if (brother.data<grandpa.data){
grandpa.left=brother;
}else {
grandpa.right=brother;
}
brother.parent=grandpa;
if (left){
brother.left=parent;
}else {
brother.right=parent;
}
parent.parent=brother;
TreeNode nil=new TreeNode(BLACK,null,parent,true);
parent.left=nil;
parent.right=nil;
//并调换颜色
brother.color=RED;
duiNephew.color=BLACK;
parent.color=BLACK;
}else if (!shunNephew.isBlack()){//2.1.2 顺侄红
}else if (brother.left.isBlack()&&brother.right.isBlack()){//2.1.3 双侄黑
}
else{//兄红
}
2.1.2兄黑,顺侄红
//兄侄交替旋转,并调换颜色,就会变成对侄红,
if (brother.data< parent.data){
parent.left=shunNephew;
shunNephew.left=brother;
}else {
parent.right=shunNephew;
shunNephew.right=brother;
}
shunNephew.parent=parent;
brother.parent=shunNephew;
TreeNode nil=new TreeNode(BLACK,null,brother,true);
brother.left=nil;
brother.right=nil;
brother.color=RED;
shunNephew.color=BLACK;
delete(data);
2.1.3 兄黑,双侄黑
//兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
brother.color=RED;
parent.color=BLACK;
TreeNode nil=new TreeNode(BLACK,null,parent,true);
if (find.data< parent.data){
parent.left=nil;
}else {
parent.right=nil;
}
删除-2.单个黑结点 2.2兄红
//父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
TreeNode grandpa=parent.parent;
if (brother.data<grandpa.data){
grandpa.left=brother;
}else {
grandpa.right=brother;
}
brother.parent=grandpa;
TreeNode tmp;
if (data<parent.data){
tmp=brother.left;
brother.left=parent;
}else {
tmp=brother.right;
brother.right=parent;
}
parent.parent=brother;
if (data<parent.data){
parent.left=find;
parent.right=tmp;
}else {
parent.left=tmp;
parent.right=find;
}
brother.color=BLACK;
parent.color=RED;
delete(data);
测试删除
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
// testAdd(tree);
initData(tree);
testDelete(tree);
tree.levelOrder(tree.root);
System.out.println(isValidRedBlackTree(tree.root));
}
private static void testDelete(RedBlackTree tree) {
tree.delete(262);//1
tree.delete(818);//1
tree.delete(705);//3
tree.delete(369);//3
tree.add(346);
tree.delete(430);//4
tree.delete(594);//2.1.1
tree.add(570);
tree.delete(485);//2.1.1
tree.add(565);
tree.delete(499);//2.1.2
tree.add(335);
tree.delete(345);//2.1.2
tree.delete(559);//2.1.3
tree.delete(570);
tree.delete(565);//2.2
tree.delete(37);
tree.delete(131);
tree.delete(95);//2.2
}
附录源码
package test;
import java.util.LinkedList;
import java.util.Queue;
public class RedBlackTree {
private static final boolean RED = false;
private static final boolean BLACK = true;
//根结点
TreeNode root;
public void initRoot(int data) {
root = new TreeNode(BLACK, data, null, false);
TreeNode nil = new TreeNode(BLACK, null, root, true);
root.left = nil;
root.right = nil;
}
/**
* 增加
* 1. 父为黑,直接插入
* 2. 父叔为红,颜色调换
* 3. 父红叔黑,颜色调换,再移动
* 4. 父子不同向,先掰直,再执行第3种情况
*
* @param data 数据
*/
public void add(int data) {
if (root == null) {
initRoot(data);
return;
}
TreeNode find = find(data);
if (!find.isLeaf) {
System.out.println("不能插入相同数据的结点");
return;
}
TreeNode parent = find.parent;
TreeNode newNode = new TreeNode(RED, data, parent);
TreeNode nil = new TreeNode(BLACK, null, newNode, true);
newNode.left = nil;
newNode.right = nil;
if (data < parent.data) {
parent.left = newNode;
} else {
parent.right = newNode;
}
//1.父为黑,直接插入
if (parent.isBlack()) {
//不需要额外的操作
} else {
TreeNode grandpa = parent.parent;
TreeNode uncle = grandpa.left != parent ? grandpa.left : grandpa.right;
//2. 父叔为红,颜色调换
if (!uncle.isBlack()) {
parent.color = BLACK;
uncle.color = BLACK;
grandpa.color = RED;
//如果爷爷是根节点
if (grandpa == root) {
grandpa.color = BLACK;
return;
}
//爷爷变成红色后,它的父叔可能为红
TreeNode cur=grandpa;
parent=cur.parent;
grandpa=parent.parent;
if (parent==null||grandpa==null){
return;
}
uncle=grandpa.left != parent ? grandpa.left : grandpa.right;
while (!cur.isBlack()&&!parent.isBlack()&&!uncle.isBlack()){
parent.color = BLACK;
uncle.color = BLACK;
grandpa.color = RED;
//如果爷爷是根节点
if (grandpa == root) {
grandpa.color = BLACK;
break;
}
cur=grandpa;
parent=cur.parent;
grandpa=parent.parent;
uncle=grandpa.left != parent ? grandpa.left : grandpa.right;
}
} else {
boolean right1 = data > parent.data;
boolean right2 = parent.data > grandpa.data;
boolean direct1 = right1 && right2;
boolean left1 = data < parent.data;
boolean left2 = parent.data < grandpa.data;
boolean direct2 = left1 && left2;
//3. 父红叔黑,颜色调换,再移动
if (direct1 || direct2) {
op(data, parent, grandpa);
} else {
//4. 父子不同向,先掰直,再执行第3种情况
if (left1) {
grandpa.right = newNode;
newNode.right = parent;
}
if (right1) {
grandpa.left = newNode;
newNode.left = parent;
}
newNode.parent = grandpa;
parent.parent = newNode;
TreeNode newNil = new TreeNode(BLACK, null, parent, true);
parent.left = newNil;
parent.right = newNil;
op(parent.data, newNode, grandpa);
}
}
}
}
public void op(int data, TreeNode parent, TreeNode grandpa) {
boolean right1 = data > parent.data;
boolean right2 = parent.data > grandpa.data;
boolean direct1 = right1 && right2;
boolean left1 = data < parent.data;
boolean left2 = parent.data < grandpa.data;
boolean direct2 = left1 && left2;
boolean tmp = grandpa.color;
grandpa.color = parent.color;
parent.color = tmp;
TreeNode grandpaPa = grandpa.parent;
if (parent.data < grandpaPa.data) {
grandpaPa.left = parent;
} else {
grandpaPa.right = parent;
}
parent.parent = grandpaPa;
if (direct1) {
parent.left = grandpa;
grandpa.parent = parent;
} else if (direct2) {
parent.right = grandpa;
grandpa.parent = parent;
}
grandpa.left = new TreeNode(BLACK, null, grandpa, true);
grandpa.right = new TreeNode(BLACK, null, grandpa, true);
}
/**
* 删除
* 1.单个红节点 直接删除
* 2.单个黑节点
* 2.1兄黑
* 2.1.1 对侄红 (指方向相反的侄节点)
* 父兄交替旋转、然后按父红兄弟黑换色 (最后一步的换色,父红两兄弟黑,是按交替旋转之后的关系处理。)
* 2.1.2 顺侄红(指方向相同的侄节点)
* 兄侄交替旋转,并调换颜色,就会变成对侄红,然后按2.1.1处理
* 2.1.3 双侄黑
* 兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
*
* 2.2 兄红
* 父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
* 3.带有一个子节点(当一个节点只有一个子节点时(空叶子除外),该节点必定是黑节点,其子节点必定是红色)
* 用红子节点值替换,然后直接删除红子节点
* 4.带有两个子节点
* 找到左子树中最靠右的子节点,用该节点值替换,并删除该节点按情况1,2,3处理(左子树中最大的值,也是离其最近的值)
*
* @param data 数据
*/
public void delete(int data) {
TreeNode find = find(data);
if (find.isLeaf){
System.out.println("没有找到");
return;
}
//1.单个红节点
if (!find.isBlack()){
if (find.left.isLeaf&&find.right.isLeaf){
TreeNode parent = find.parent;
TreeNode nil=new TreeNode(BLACK,null,parent,true);
if (data<parent.data){
parent.left=nil;
}else {
parent.right=nil;
}
}else {
//4.带有两个子节点
TreeNode replace = findReplace(find);
delete(replace.data);
find.data= replace.data;
}
}else {
//3.带有一个子节点,用红子节点值替换,然后直接删除红子节点
if (find.left.isLeaf&&!find.right.isBlack()){
find.data=find.right.data;
find.right= new TreeNode(BLACK,null,find,true);
}else if (find.right.isLeaf&&!find.left.isBlack()){
find.data=find.left.data;
find.left= new TreeNode(BLACK,null,find,true);
} else if (!find.left.isLeaf&&!find.right.isLeaf){//4.带有两个子节点
TreeNode replace = findReplace(find);
delete(replace.data);
find.data= replace.data;
}else {//2.单个黑结点
TreeNode parent=find.parent;
TreeNode brother=parent.left!=find?parent.left:parent.right;
boolean left=find.data<parent.data;
//对侄
TreeNode duiNephew=left?brother.right:brother.left;
//顺侄
TreeNode shunNephew=left?brother.left:brother.right;
if (brother.isBlack()){//2.1兄黑
//2.1.1 对侄红
if (!duiNephew.isBlack()){
//父兄交替旋转
TreeNode grandpa=parent.parent;
if (brother.data<grandpa.data){
grandpa.left=brother;
}else {
grandpa.right=brother;
}
brother.parent=grandpa;
if (left){
brother.left=parent;
}else {
brother.right=parent;
}
parent.parent=brother;
TreeNode nil=new TreeNode(BLACK,null,parent,true);
parent.left=nil;
parent.right=nil;
//并调换颜色
brother.color=RED;
duiNephew.color=BLACK;
parent.color=BLACK;
} else if (!shunNephew.isBlack()){//2.1.2 顺侄红
//兄侄交替旋转,并调换颜色,就会变成对侄红,
if (brother.data< parent.data){
parent.left=shunNephew;
shunNephew.left=brother;
}else {
parent.right=shunNephew;
shunNephew.right=brother;
}
shunNephew.parent=parent;
brother.parent=shunNephew;
TreeNode nil=new TreeNode(BLACK,null,brother,true);
brother.left=nil;
brother.right=nil;
brother.color=RED;
shunNephew.color=BLACK;
delete(data);
} else if (brother.left.isBlack()&&brother.right.isBlack()){//2.1.3 双侄黑
//兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
brother.color=RED;
parent.color=BLACK;
TreeNode nil=new TreeNode(BLACK,null,parent,true);
if (find.data< parent.data){
parent.left=nil;
}else {
parent.right=nil;
}
}
}else {//2.2兄红
//父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
TreeNode grandpa=parent.parent;
if (brother.data<grandpa.data){
grandpa.left=brother;
}else {
grandpa.right=brother;
}
brother.parent=grandpa;
TreeNode tmp;
if (data<parent.data){
tmp=brother.left;
brother.left=parent;
}else {
tmp=brother.right;
brother.right=parent;
}
parent.parent=brother;
if (data<parent.data){
parent.left=find;
parent.right=tmp;
}else {
parent.left=tmp;
parent.right=find;
}
brother.color=BLACK;
parent.color=RED;
delete(data);
}
}
}
}
/**
* 查找
*
* @param data 数据
* @return 查找结点,如果差不到就会返回叶子结点
*/
public TreeNode find(int data) {
TreeNode find = root;
while (!find.isLeaf) {
if (data < find.data) {
find = find.left;
} else if(find.data.equals(data)){
return find;
} else if (data > find.data) {
find = find.right;
}
}
return find;
}
/**
* 查找替代的结点
* 中序遍历线索树的直接前驱结点
* @param node 删除的结点
* @return 查找替代
*/
public TreeNode findReplace(TreeNode node) {
TreeNode left = node.left;
while (!left.isLeaf) {
left=left.right;
}
return left.parent;
}
//中序遍历
public void inorder(TreeNode root) {
if (root == null) {
return;
}
if (!root.left.isLeaf) {
inorder(root.left);
}
System.out.println(root);
if (!root.right.isLeaf) {
inorder(root.right);
}
}
//层序遍历
public void levelOrder(TreeNode root) {
if (root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode cur = queue.poll();
System.out.print(cur + "\t");
if (cur.left != null) {
queue.add(cur.left);
}
if (cur.right != null) {
queue.add(cur.right);
}
}
System.out.println();
}
}
private static int blackHeight = -1;
//判断是否是有效的红黑树
public static boolean isValidRedBlackTree(TreeNode root) {
if (root == null) {
return true;
}
// 检查根节点是否是黑色
if (root.color != BLACK) {
return false;
}
// 计算黑高度,并检查红黑平衡
blackHeight = -1;
if (!checkBlackBalance(root, 0)) {
return false;
}
// 递归检查每个节点
return isValidRedBlackSubtree(root);
}
private static boolean checkBlackBalance(TreeNode node, int currentBlackHeight) {
if (node.isLeaf) {
if (blackHeight == -1) {
blackHeight = currentBlackHeight;
return true;
} else {
return currentBlackHeight == blackHeight;
}
}
if (node.color == BLACK) {
currentBlackHeight++;
}
return checkBlackBalance(node.left, currentBlackHeight) && checkBlackBalance(node.right, currentBlackHeight);
}
private static boolean isValidRedBlackSubtree(TreeNode node) {
if (node == null) {
return true;
}
// 检查红黑树性质
if (node.color == RED) {
if ((node.left != null && node.left.color != BLACK) || (node.right != null && node.right.color != BLACK)) {
return false;
}
}
// 递归检查左右子树
return isValidRedBlackSubtree(node.left) && isValidRedBlackSubtree(node.right);
}
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
// testAdd(tree);
initData(tree);
testDelete(tree);
tree.levelOrder(tree.root);
System.out.println(isValidRedBlackTree(tree.root));
}
private static void testDelete(RedBlackTree tree) {
tree.delete(262);//1
tree.delete(818);//1
tree.delete(705);//3
tree.delete(369);//3
tree.add(346);
tree.delete(430);//4
tree.delete(594);//2.1.1
tree.add(570);
tree.delete(485);//2.1.1
tree.add(565);
tree.delete(499);//2.1.2
tree.add(335);
tree.delete(345);//2.1.2
tree.delete(559);//2.1.3
tree.delete(570);
tree.delete(565);//2.2
tree.delete(37);
tree.delete(131);
tree.delete(95);//2.2
}
private static void initData(RedBlackTree tree) {
int[] nums={430,261,636,
95,344,559,822,
37,131,330,369,499,594,705,981,
262,345,485,664,818};
for (int i = 0; i < nums.length; i++) {
tree.add(nums[i]);
}
// tree.inorder(tree.root);
// tree.levelOrder(tree.root);
// System.out.println(isValidRedBlackTree(tree.root));
}
private static void testAdd(RedBlackTree tree) {
tree.add(157);//0
tree.add(12);//1
tree.add(200);//1
tree.add(250);//2
tree.add(260);//3
tree.add(220);//2
tree.add(210);//4
tree.add(11);//1
tree.add(10);//3
tree.add(7);//2
tree.add(9);//4
tree.inorder(tree.root);
// tree.levelOrder(tree.root);
System.out.println(isValidRedBlackTree(tree.root));
}
}
//结点
class TreeNode {
//true是黑色,false是红色
boolean color;
//数据
Integer data;
TreeNode left;
TreeNode right;
private static final boolean RED = false;
private static final boolean BLACK = true;
//是否是叶子结点
boolean isLeaf;
//方便实现
TreeNode parent;
public TreeNode() {
}
public TreeNode(int data) {
this.data = data;
}
public TreeNode(boolean color, Integer data) {
this.color = color;
this.data = data;
}
public TreeNode(boolean color, Integer data, TreeNode parent) {
this.color = color;
this.data = data;
this.parent = parent;
}
public TreeNode(boolean color, Integer data, TreeNode parent, boolean isLeaf) {
this.color = color;
this.data = data;
this.parent = parent;
this.isLeaf = isLeaf;
}
// public TreeNode(Integer data,TreeNode left, TreeNode right) {
// this.data = data;
// this.left = left;
// this.right = right;
// }
public boolean isBlack() {
return color == BLACK;
}
@Override
public String toString() {
return "TreeNode{" +
"color=" + color +
", data=" + data +
'}';
}
}
最后
2024-3-30 23:05:13
写了一天
迎着日光月光星光,直面风霜雨霜雪霜。