手写红黑树【数据结构】
手写红黑树【数据结构】
- 前言
- 版权
- 推荐
- 手写红黑树
- 一、理论知识
- 红黑树的特征
- 增加
- 删除
- 二、手写代码
- 初始-树结点
- 初始-红黑树
- 初始-遍历
- 初始-判断红黑树是否有效
- 查找
- 增加-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;}@Overridepublic 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);//0tree.add(12);//1tree.add(200);//1tree.add(250);//2tree.add(260);//3tree.add(220);//2tree.add(210);//4tree.add(11);//1tree.add(10);//3tree.add(7);//2tree.add(9);//4tree.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);//1tree.delete(818);//1tree.delete(705);//3tree.delete(369);//3tree.add(346);tree.delete(430);//4tree.delete(594);//2.1.1tree.add(570);tree.delete(485);//2.1.1tree.add(565);tree.delete(499);//2.1.2tree.add(335);tree.delete(345);//2.1.2tree.delete(559);//2.1.3tree.delete(570);tree.delete(565);//2.2tree.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);//1tree.delete(818);//1tree.delete(705);//3tree.delete(369);//3tree.add(346);tree.delete(430);//4tree.delete(594);//2.1.1tree.add(570);tree.delete(485);//2.1.1tree.add(565);tree.delete(499);//2.1.2tree.add(335);tree.delete(345);//2.1.2tree.delete(559);//2.1.3tree.delete(570);tree.delete(565);//2.2tree.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);//0tree.add(12);//1tree.add(200);//1tree.add(250);//2tree.add(260);//3tree.add(220);//2tree.add(210);//4tree.add(11);//1tree.add(10);//3tree.add(7);//2tree.add(9);//4tree.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;}@Overridepublic String toString() {return "TreeNode{" +"color=" + color +", data=" + data +'}';}}
最后
2024-3-30 23:05:13
写了一天
迎着日光月光星光,直面风霜雨霜雪霜。
相关文章:
手写红黑树【数据结构】
手写红黑树【数据结构】 前言版权推荐手写红黑树一、理论知识红黑树的特征增加删除 二、手写代码初始-树结点初始-红黑树初始-遍历初始-判断红黑树是否有效查找增加-1.父为黑,直接插入增加-2. 父叔为红,颜色调换增加-3. 父红叔黑,颜色调换&am…...
[蓝桥杯练习]通电
kruskal做法(加边) #include <bits/stdc.h> using namespace std; int x[10005],y[10005],z[10005];//存储i点的x与y坐标 int bcj[10005];//并查集 struct Edge{//边 int v1,v2; double w; }edge[2000005]; int cmp(Edge a, Edge b){return a.w < b.w;} int find(i…...
安全算法 - 摘要算法
摘要算法是一种将任意长度的数据转换为固定长度字节串的算法。它具有以下特点和应用。 首先,摘要算法能够生成一个唯一且固定长度的摘要值,用于验证数据的完整性和一致性。无论输入数据有多长,生成的摘要值始终是固定长度的,且即…...
操作系统:动静态库
目录 1.动静态库 1.1.如何制作一个库 1.2.静态库的使用和管理 1.3.安装和使用库 1.4.动态库 1.4.1.动态库的实现 1.4.2.动态库与静态库的区别 1.4.3.共享动态库给系统的方法 2.动态链接 2.1.操作系统层面的动态链接 1.动静态库 静态库(.a)&…...
车载电子电器架构 —— 局部网络管理汇总
车载电子电器架构 —— 局部网络管理汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…...
网络安全 | 什么是DDoS攻击?
关注WX:CodingTechWork DDoS-介绍 DoS:Denial of Service,拒绝服务。DDoS是通过大规模的网络流量使得正常流量不能访问受害者目标,是一种压垮性的网络攻击,而不是一种入侵手段。NTP网络时间协议,设备需要…...
[Godot] 3D拾取
CollisionObject3D文档 Camera3D文档 CollisionObject3D有个信号_input_event,可以用于处理3D拾取。 Camera3D也有project_position用于将屏幕空间坐标投影到3D空间。 extends Node3D#是否处于选中状态 var selected : bool false #摄像机的前向量 var front : V…...
知识融合:知识图谱构建的关键技术
目录 一、引言二、知识图谱基础2.1 知识表示三元组属性图 2.2 知识抽取实体抽取关系抽取属性抽取 三、知识融合的核心问题3.1 实体识别与链接实体识别实体链接 3.2 重复实体合并方法示例 3.3 关系融合挑战方法示例 四、知识融合技术深度解析4.1 基于规则的方法规则设计原则规则…...
外贸建站:WordPress搭建外贸独立站零基础自建站完整教程(2024)
对于做外贸来说,拥有自己的外贸独立网站真的非常重要。在外贸领域,如今各平台竞争激烈,规则多,成本高,价格战、政策变化快,还存在封店风险等等因素。在这种情况下,拥有外贸独立站就能很好规避上…...
【教程】Kotlin语言学习笔记(五)——Lambda表达式与条件控制
写在前面: 如果文章对你有帮助,记得点赞关注加收藏一波,利于以后需要的时候复习,多谢支持! 【Kotlin语言学习】系列文章 第一章 《认识Kotlin》 第二章 《数据类型》 第三章 《数据容器》 第四章 《方法》 第五章 《L…...
C++的并发世界(三)——线程对象生命周期
0.案例代码 先看下面一个例子: #include <iostream> #include <thread>void ThreadMain() {std::cout << "begin sub thread:" << std::this_thread::get_id()<<std::endl;for (int i 0; i < 10; i){std::cout <&…...
SAD法(附python实现)和Siamese神经网络计算图像的视差图
1 视差图 视差图:以左视图视差图为例,在像素位置p的视差值等于该像素在右图上的匹配点的列坐标减去其在左图上的列坐标 视差图和深度图: z f b d z \frac{fb}{d} zdfb 其中 d d d 是视差, f f f 是焦距, b b…...
基于DWT(离散小波变换)的图像加密水印算法,Matlab实现
博主简介: 专注、专一于Matlab图像处理学习、交流,matlab图像代码代做/项目合作可以联系(QQ:3249726188) 个人主页:Matlab_ImagePro-CSDN博客 原则:代码均由本人编写完成,非中介,提供…...
【威胁情报综述阅读3】Cyber Threat Intelligence Mining for Proactive Cybersecurity Defense
【威胁情报综述阅读1】Cyber Threat Intelligence Mining for Proactive Cybersecurity Defense: A Survey and New Perspectives 写在最前面一、介绍二、网络威胁情报挖掘方法和分类A. 研究方法1) 第 1 步 - 网络场景分析:2) 第 2 步 - 数据…...
在编程中使用中文到底该不该??
看到知乎上有个热门问题,为什么很多人反对中文在编程中的使用? 这个问题有几百万的浏览热度,其中排名第一的回答非常简洁,我深以为然: 在国内做开发,用中文写注释、写文档,是非常好的习惯&…...
PyQt6从入门到放弃
PyQt6从入门到放弃 安装PyQt6 pip install PyQt6# 查看QT和PyQT的版本 from PyQt6.QtCore import QT_VERSION_STR from PyQt6.QtCore import PYQT_VERSION_STR print(QT_VERSION_STR) print(PYQT_VERSION_STR)PyQt6模块 PyQt6类由一系列模块组成包括QtCore、QtGui、QtWidgets…...
PhpWord导入试卷
规定word导入格式 1、[单选题][2024][一般]题目1 A.选项1 B.选项2 C.选项3 D.选项4 答案:D 试题图片(上传多媒体图片): 分数:2 答案解析: 2、[多选题][2024][困难]题目2 A.选项1 B.选项2 C.选项3 D.选项4 E…...
C# 运算符重载 之前的小总结
C# 中支持运算符重载,所谓运算符重载就是我们可以使用自定义类型来重新定义 C# 中大多数运算符的功能。运算符重载需要通过 operator 关键字后跟运算符的形式来定义的,我们可以将被重新定义的运算符看作是具有特殊名称的函数,与其他函数一样&…...
XenCenter 2024 创建一个虚拟机
前言 实现,创建一个虚拟机,内存,cpu,磁盘,名称,网卡,配置 Xen Center 2024 download 创建虚拟机 选择系统类型 定义虚拟机名称 选择ISO镜像库 选择主服务器 分配虚拟机内存,cpu资源…...
tomcat 知多少
Tomcat的缺省端口: 默认端口为8080,可以通过在tomcat安装包conf目录下,service.xml中的Connector元素的port属性来修改端口。 tomcat 常见 Connector 运行模式(优化): 这三种模式的不同之处如下: BIO : 一…...
【详细讲解语言模型的原理、实战与评估】
🌈个人主页:程序员不想敲代码啊🌈 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家🏆 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提…...
Predict the Next “X” ,第四范式发布先知AIOS 5.0
今天,第四范式发布了先知AIOS 5.0,一款全新的行业大模型平台。 大语言模型的原理是根据历史单词去不断预测下一个单词,换一句常见的话:Predict the Next “Word”。 当前对于行业大模型的普遍认知就是沿用这种逻辑,用大…...
PCL使用4PCS配准
一、代码 C++ #include <pcl/registration/ia_fpcs.h> // 4PCS算法 #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> #include <pcl/io/ply_io.h> #include <boost/thread/thread.hpp> #include…...
【六 (2)机器学习-机器学习建模步骤/kaggle房价回归实战】
一、确定问题和目标: 1、业务需求分析: 与业务团队或相关利益方进行深入沟通,了解他们的需求和期望。 分析业务流程,找出可能的瓶颈、机会或挑战。 思考机器学习如何帮助解决这些问题或实现业务目标。 2、问题定义:…...
vue源码解析——vue如何将template转换为render函数
Vue 将模板(template)转换为渲染函数(render function)是 Vue 编译器的核心功能,它是 Vue 实现响应式和虚拟 DOM 的关键步骤。在 Vue 中,模板(template)是开发者编写的类似 HTML 的代…...
深入理解zookeeper
如果是zookeeper的初学者,可以看: zookeeper快速入门(合集)-CSDN博客 如果想要深入理解zookeeper,并在面试中取得更好的表现,可以看下面的文章,都是偏面试向的角度写的。 三分钟明白zookeeper…...
【漏洞复现】WordPress Plugin LearnDash LMS 敏感信息暴漏
漏洞描述 WordPress和WordPress plugin都是WordPress基金会的产品。WordPress是一套使用PHP语言开发的博客平台。该平台支持在PHP和MySQL的服务器上架设个人博客网站。WordPress plugin是一个应用插件。 WordPress Plugin LearnDash LMS 4.10.2及之前版本存在安全漏洞&#x…...
phpmyadmin页面getshell
0x00 前言 来到phpmyadmin页面后如何getshell呢?下面介绍两种方法 0x01 select into outfile直接写入 1、利用条件 对web目录需要有写权限能够使用单引号(root) 知道网站绝对路径(phpinfo/php探针/通过报错等) secure_file_priv没有具体值 2、查看secure_file…...
题目:学习static定义静态变量的用法
题目:学习static定义静态变量的用法 There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheate…...
【C++】编程规范之函数规则
对所有函数入参进行合法性检查 在编写函数时,应该始终对所有传入的参数进行合法性检查,以防止出现意外的错误或异常情况。这包括但不限于检查指针是否为空、整数是否在有效范围内、数组是否越界等等。通过对参数进行严格的合法性检查,可以避免…...
wordpress全站pjax/百度公司招聘信息
持续更新中。。。 尝试用蓝牙hid协议 文章目录1.个人实践1.1 app在后台处于没有被系统杀死时,可以自动重连和发数据1.2 但在后台被系统杀死(没有被收到杀死),当连接状态发生变化的时候,系统会唤醒app,扫描后会调用1.3 因为后台获取位置时&…...
网站制作过程/北京如何优化搜索引擎
现在支持GLSL和OpenGL跟步调试的只有Nvidia的Nsight,只支持Nvidia的显卡;其他的基本都是track,不支持GLSL的跟步调试,比如AMD的GPUPerfClient以及gDEBugger。还有AMD的GPU ShaderAnalyzer也非常的不错,能看到相应的GLS…...
外贸婚纱网站 侵权/产品如何推广
Lunix Shell编程入门 前言 要想玩转类Unix系统,仅仅会基本的常用操作命令还是远远不够的,本文介绍lunix系统下的Shell编程,通过一系列的简单Shell代码示例,一步步入门Shell编程。 一、Shell是什么? Linux系统被比作…...
垃圾网站建设/关键词seo报价
Linux基础实验 过滤,管道与重定向 过滤管道重定向运维常用 给某目录设置权限 sudo chmod -R 777 /var/www/html/ posted 2018-01-30 01:03 douzujun 阅读(...) 评论(...) 编辑 收藏...
网站建设费用分录/口碑营销什么意思
http://baike.baidu.com/view/1234431.htm转载于:https://blog.51cto.com/yerik/757540...
微网站开发技术架构/淘宝怎么推广自己的产品
SourceTree0、前言1、SourceTree | Github Desktop | TortoiseGit 可视化管理工具对比Github DesktopTortoiseGitSourcetree2、SourceTree可视化管理工具安装及配置SourceTree简介SourceTree下载地址SourceTree Atlassian账号注册地址:选择分布式版本控制系统&#…...