【高阶数据结构】揭开红黑树‘恶魔’的面具:深度解析底层逻辑

在这里插入图片描述

高阶数据结构相关知识点可以通过点击以下链接进行学习一起加油!
二叉搜索树AVL树

大家好,我是店小二,欢迎来到本篇内容!今天我们将一起探索红黑树的工作原理及部分功能实现。红黑树的概念相对抽象,但只要我们一步步深入,定能慢慢揭开它的神秘面纱

请添加图片描述
Alt
🌈个人主页:是店小二呀
🌈C语言专栏:C语言
🌈C++专栏: C++
🌈初阶数据结构专栏: 初阶数据结构
🌈高阶数据结构专栏: 高阶数据结构
🌈Linux专栏: Linux

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

  • 一、红黑树概念
    • 1.1 最短和最长路径
  • 二、红黑树的节点部分
  • 三、关于红黑树的插入
    • 3.1 对某个红黑树进行分析(难点)
    • 3.2 红黑树插入逻辑(重点)
  • 四、遍历红黑树
  • 五、判断是否满足红黑树
  • 六、红黑树的删除
  • 七、红黑树与AVL树的比较
  • 八、RBTree.h

一、红黑树概念

红黑树,是一种二叉搜索树,但在每个节点上增加一个存储位表示节点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

在这里插入图片描述

红黑树的性质

  1. 每个节点不是红色就是黑色

  2. 根节点是黑色的

  3. 如果一个节点是红色的,则它的两个孩子节点是黑色

  4. 对于每个节点,从该节点到其后代节点的简单路径上,均包含相同数目的黑色节点

  5. 每个叶子节点都是黑色的(此处的叶子节点指的是空节点)

满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍,但是不能保证满足这样的条件就是红黑树,具体还是往下看吧,这里重点关注的是规则3、4。

1.1 最短和最长路径

  • 最短:全黑

  • 最长:一黑一红

  • 虽然有最短和最长路径,但是不一定存在最短或最长路径

在这里插入图片描述

具体说明:

  1. 对于红黑树满足最长路径中节点个数不会超过最短路径节点个数的两倍结论,是根据红黑树规则及其最短和最长路径得来的。
  2. 规则四:对于每个节点,从该节点到其后代节点的简单路径上,均包含相同数目的黑色节点。保证了每条路径黑色节点数量相同的。如果我们想要延长某一条路径,插入黑色节点不能达成目的,对此我们需要插入红色节点,碍于规则三:如果一个节点是红色的,则它的两个孩子节点是黑色的限制,对此只能在两个黑色节点之间插入一个红色节点或者在最后黑色节点插入一个红色节点。导致了最短为全黑,最长一黑一红,大致形成两倍的关系,但是不一定存在最短或最长路径(路径相同)。
  3. 红黑树的平衡维护在这微妙的关系中,如果不能保证其最长路径中节点个数不会超过最短路径节点个数的两倍就是不是红黑树,就是能保证也不一定是红黑树,不充分必要条件
  4. 使用枚举常量代替红色和黑色的含义。就是跟全部黑色节点或者全部黑色节点配合红色节点

二、红黑树的节点部分

enum Color
{RED, BLACK
};template<class K, class V> struct RBTreeNode{RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;pair<K, V> _kv;Color _color;RBTreeNode(const pair<K, V>& kv):_parent(nullptr),_left(nullptr),_right(nullptr),_kv(kv),_color(RED){}};

三、关于红黑树的插入

节点的插入无非两种颜色,要么红色要么黑色。如果使用严母慈父来说规则3、4的严格程度,规则3是慈父,规则4是严母。如果插入节点为黑色,必然会违法规则4,每条都会收到影响;如果插入的节点为红色,可能会违法规则3(存在连续的两个红色节点)

在这里插入图片描述

这样子不管插入啥颜色都会违法规则,如果一定要插入优选红色节点(慈父身份),接下来让我们来看,如果处理插入为红色节点,出现连续红色的问题吧。

在这里插入图片描述

3.1 对某个红黑树进行分析(难点)

前文(主要是给自己看,不懂可以评论区见)

关于这一点还是挺晕的,以下是我的看法:

  • 出现问题情况就是发生在cur和parent为红色情况下,至于为什么是在cur和parent为红色情况下。我们进行红黑树插入节点到红黑树调整结构,都是从下到上的。要么parent是黑的,cur是红的,插入没有问题;要么parent是红的,cur是黑的,插入违法规则四,不如违法规则三。所以就需要考虑cur和parent是红色的就行。
  • 既然cur和parent及其grandfather颜色是确定,问题在于uncle颜色上,所以我们分出了很多种情况,去特殊处理。
  • 规则四只是保证每条路径黑色节点数量相同,没有保证在同一层,对此在调整过程中,u就是未知的颜色,u是黑色还是不存在,我们需要根据规则去还源之前的场景,是第一种情况基础上打造了第二种情况合并为一整棵红黑树,这也是属于众多红黑树的部分。将这些特殊红黑树处理好,面对不同场景,特殊进行处理。
  • 也就是说,也是整棵红黑树的一部分

在这里插入图片描述


正文
这里我们可以确定c\p\g颜色是确定的,关键在于u颜色是不确定,从而影响整棵树的平衡

这里将情况分为两种

  1. 情况一:cur为红,p为红,g为黑,u存在且为红

  2. 情况二:cur为红,p为红,g为黑,u不存在/u存在且为黑

情况一

处理办法:将p、u改为黑,g改为红。具体流程在图中(将cur赋值给g)

在这里插入图片描述

关于红黑树分析将采取抽象图进行分析,其实这里也可以看出具体图。还是以小部分组成为大部分的思想,可以根据下图理解这个思想,就是这个结构在上面重演了。

在这里插入图片描述

在这里插入图片描述

如果在一颗高度很高红黑树中,我们可以看见红黑树在调整过程中会不断重复熟悉的场景,所以抽象图中的具体部分就被包含在其中。

第二种情况

在这里插入图片描述

对于这两种情况,就是在变色过程中,违法了规则四(严母)。如果p变色了u还是黑色或者不存在,会导致每条路径上的黑色节点个数不相等,对此跟插入黑色节点一样。

在这里插入图片描述

接下来,需要建立在这两张图进行说明,不然很容易晕的(重点)

  1. 如果u节点不存在,则cur一定是新节点。假设u节点存在,颜色为黑色,cur和p为了维护规则四,要么cur是黑色,后面跟着两个红色节点,要么p是黑色,规则四成立。然后将u节点拿出去,cur如果不是新节点,一切全部不成立,规则四直接违法。
  2. 如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定黑色,为了遵守规则四。现在看到其是红色的原因是由于cur子树在调整的过程中将cur节点的颜色由黑色改为红色。

在这里插入图片描述

上面两种情况是根据u变量无法确认是红色还是黑色,导致影响不同。

在第二种情况的基础上,还有一个小问题就是连续红色节点不是一边独红,需要先旋转进行处理,调整到一边连续红色节点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里将根据思路和不同功能罗列代码了,这里旋转后的节点的颜色需要自己根据图单独进行处理的。红黑树也是属于二叉搜索树,对此我们这里可以CV下代码。

右旋代码

void RotateR(Node* parent)
{Node* SubL = parent->_left;Node* SubLR = SubL->_right;parent->_left = SubLR;if (SubLR)SubLR->_parent = parent;SubL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = SubL;if (parent == _root){SubL = _root;SubL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubL;}else if (ppNode->_right == parent){ppNode->_right = SubL;}SubL->_parant = ppNode;}
}

左旋代码

void RotateL(Node* parent)
{Node* SubR = parent->_right;Node* SubRL = SubR->_left;parent->_right = SubRL;if (SubRL)SubR->_parent = parent;SubR->_left = parent;Node* ppNode = parent->_parent;parent->_parent = SubR;if (parent == _root){SubR = _root;SubR->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubR;}else if (ppNode->_right == parent){ppNode->_right = SubR;}SubR->_parant = ppNode;}
}

3.2 红黑树插入逻辑(重点)

typedef RBTreeNode<K, V>  Node;
public:bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{assert(!cur);}}//需要将这个节点连接起来cur = new Node(kv);cur->_color = RED;cur->_parent = parent;if (parent->kv.first > cur->_kv.first){parent->_left = cur;}else if (parent->kv.first < cur->_kv.first){parent->_right = cur;}//根据不同情况就行调整,父亲节点是黑色就是走到根了while (parent && parent->_color == RED){Node* grandfather = parent->_parent;//这里默认插入都是红色,这里需要进行调整if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle == RED){parent->_color = uncle->_color = BLACK;grandfather = RED;cur = grandfather;parent = cur->_parent;}//uncle不存在或者为黑色,需要旋转else{if (cur = parent->_left)//一边红的情况{RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{RotateL(parent);RotateR(grandfather);cur = BLACK;parent->_color = grandfather->_color = RED;}}}else{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色//      g//   u     p//            cif (cur == parent->_right)//一边红的情况{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}//不管根是什么颜色,都修改一遍为黑色_root->_col = BLACK;return true;
}

四、遍历红黑树

如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

void InOder()
{_InOder(_root);cout << endl;
}
private:	
void _InOder(Node* _root)
{_InOder(_root->_left);cout << endl;_InOder(_root->_right);}

这里将中序遍历实现逻辑封装到私有成员函数中隐藏它的具体实现细节,使得外部用户无法直接访问该函数,提高了代码的安全性和可维护性,符合面向对象编程的封装性原则。这里外部访问中序遍历接口,只是传递了节点指针的副本,而不修改任何节指针的实际值。

五、判断是否满足红黑树

可以根据红黑树的规则来进行判断,具体细节在代码块注释。其中规则四就是判断每条路径的黑色节点个数是否相同,那么可以提前记录好一条路径的黑色节点个数去比较,当_root == nullptr说明这条路径遍历完毕,需要就行判断

bool IsBalance() {//规则一:根节点颜色为黑色if (_root->_color == RED){return false;}int refNum = 0;//记录单独一边黑色节点个数Node* cur = _root;while (cur){refNum++;cur = cur->_left;}return Check(_root, 0, refNum);}
private:bool Check(Node* root, int blackNum, const int refNum)
{//接下就是遍历每条路径了if (_root == nullptr){//规则4if (blackNum != refNum){cout << "不满足每条路径相同黑色节点" << endl;return  false;}return true;}//规则3if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在连续的红色节点" << endl;return false;}//记录每条路径的黑色节点//可行的原因是该blackNum传递形参//每个节点记录一个值:根到当前节点路径中黑色节点的数量if (root->_col == BLACK){blackNum++;}return Check(root->_left , blackNum,refNum) && Check(root->_right, blackNum, refNum)
}

关于规则3这里判断逻辑设计,是根找父亲,而不是根找儿子。如果是根找儿子,需要去左右孩子去找又分为多种情况,不然选择一条单一的路线,根找父亲,反正都是遍历每个节点。

在这里插入图片描述

问题:

  • 为什么不能通过其最长路径中节点个数不会超过最短路径节点个数的两倍来判断是否为红黑树呢?

答:

  • 这里是不充分必要条件,比如下图12左边这条路径不满足规则4,当插入时就会出现问题。(路径是指某条路径空节点到根节点)

在这里插入图片描述

六、红黑树的删除

红黑树的删除本节不做讲解,有兴趣的可参考:《算法导论》或者《STL源码剖析》。以下提供相关链接:红黑树删除

七、红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(logn),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多

八、RBTree.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;enum Color
{RED, BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;pair<K, V> _kv;Color _color;RBTreeNode(const pair<K, V>& kv):_parent(nullptr), _left(nullptr), _right(nullptr), _kv(kv), _color(RED){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V>  Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{assert(!cur);}}//需要将这个节点连接起来cur = new Node(kv);cur->_color = RED;cur->_parent = parent;if (parent->kv.first > cur->_kv.first){parent->_left = cur;}else if (parent->kv.first < cur->_kv.first){parent->_right = cur;}//根据不同情况就行调整,父亲节点是黑色就是走到根了while (parent && parent->_color == RED){Node* grandfather = parent->_parent;//这里默认插入都是红色,这里需要进行调整if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle == RED){parent->_color = uncle->_color = BLACK;grandfather = RED;cur = grandfather;parent = cur->_parent;}//uncle不存在或者为黑色,需要旋转else{if (cur = parent->_left){RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{RotateL(parent);RotateR(grandfather);cur = BLACK;parent->_color = grandfather->_color = RED;}}}else{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色//      g//   u     p//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;parent->_left = SubLR;if (SubLR)SubLR->_parent = parent;SubL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = SubL;if (parent == _root){SubL = _root;SubL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubL;}else if (ppNode->_right == parent){ppNode->_right = SubL;}SubL->_parant = ppNode;}}void RotateL(Node* parent){Node* SubR = parent->_right;Node* SubRL = SubR->_left;parent->_right = SubRL;if (SubRL)SubR->_parent = parent;SubR->_left = parent;Node* ppNode = parent->_parent;parent->_parent = SubR;if (parent == _root){SubR = _root;SubR->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubR;}else if (ppNode->_right == parent){ppNode->_right = SubR;}SubR->_parant = ppNode;}}bool IsBalance(){if (_root->_color == RED){return false;}int refNum = 0;//记录单独一边黑色节点个数Node* cur = _root;while (cur){refNum++;cur = cur->_left;}return Check(_root, 0, refNum);}void InOder(){_InOder(_root);cout << endl;}private:bool Check(Node* root, int blackNum, const int refNum){//接下就是遍历每条路径了if (_root == nullptr){if (blackNum != refNum){cout << "不满足每条路径相同黑色节点" << endl;return  false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在连续的红色节点" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum)}void _InOder(Node* _root){_InOder(_root->_left);cout << endl;_InOder(_root->_right);}
private:Node* _root = nullptr;
};

在这里插入图片描述

感谢大家的观看!以上就是本篇文章的全部内容。我是店小二,希望这些高阶数据结构笔记能为你在学习旅途中提供帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/450939.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flutter assets配置加载本地图片报错

首选列出我在照着网上说的设置assets怎么搞都报错&#xff0c;错误如下&#xff0c;搞的我想骂娘。 flutter: uses-material-design: true assets: - assets/images 后来找到了下面这个教程&#xff0c;才终于解决&#xff0c;就是要在后面加一个"/" 。 flutter这个…

【分布式知识】MapReduce详细介绍

文章目录 MapReduce概述1. MapReduce编程模型Map阶段Reduce阶段 2. Shuffle和Sort阶段3. MapReduce作业的执行流程4. MapReduce的优化和特性5. MapReduce的配置和调优 MapReduce局限性相关文献 MapReduce概述 MapReduce是一个分布式计算框架&#xff0c;它允许用户编写可以在大…

【热门】智慧果园管理系统解决方案

随着科技的进步,原有农业种植方式已经不能满足社会发展的需要,必须对传统的农业进行技术更新和改造。经过多年的实践,人们总结出一种新的种植方法——温室农业,即“用人工设施控制环境因素,使作物获得最适宜的生长条件,从而延长生产季节,获得最佳的产出”。这种农业生产方式…

scala 类的继承

继承的定义 idea实例 语法 重写 重写&#xff1a;在子类中重新定义父类的同名方法 idea实例 多态 多态&#xff1a;传入的对象不同&#xff0c;调用的方法的效果就不同&#xff01; 原理&#xff1a;参数是父类类型 idea实例 构造器

使用开源的 Vue 移动端表单设计器创建表单

FcDesigner Vant 版是一款基于 Vue3.0 的移动端低代码可视化表单设计器工具&#xff0c;通过数据驱动表单渲染。可以通过拖拽的方式快速创建表单&#xff0c;提高开发者对表单的开发效率&#xff0c;节省开发者的时间。 源码下载 | 演示地址 | 帮助文档 本项目采用 Vue3.0 和 …

3D医学影像开发入门<二>:VS2019+Qt5.15.2+VTK9.3.1编译及环境配置

VTK&#xff08;Visualization Toolkit&#xff09;是一个开源的、跨平台的三维可视化开发库&#xff0c;用于处理和可视化三维数据。它提供了一系列算法和工具&#xff0c;用于创建、操作和渲染复杂的三维图形&#xff0c;并支持多种数据表示方式&#xff0c;包括点、线、面、…

Spring Boot知识管理系统:用户体验设计

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

Pycharm下载安装教程(详细步骤)+汉化设置教程

今天讲解的是Pycharm安装教程和配置汉化设置&#xff0c;希望能够帮助到大家。 创作不易&#xff0c;还请各位同学三连点赞&#xff01;&#xff01;收藏&#xff01;&#xff01;转发&#xff01;&#xff01;&#xff01; 对于刚入门学习Python还找不到方向的小伙伴可以试试…

部署私有仓库以及docker web ui应用

官方地址&#xff1a;https://hub.docker.com/_/registry/tags 一、拉取registry私有仓库镜像 docker pull registry:latest 二、运⾏容器 docker run -itd -v /home/dockerdata/registry:/var/lib/registry --name "pri_registry1" --restartalways -p 5000:5000 …

Android取证简介(翻译)

在此文中&#xff0c;我们将探讨 Android 取证、获取 Android 设备的过程、反取证技术以及从 Android 设备映像分析和恢复已删除文件的实际示例。 # 本文中使用的关键术语 采集(Acquisition) &#xff1a; 在数字取证调查期间收集敏感数据 取证健全性(Forensically Soundnes…

【linux】Microsoft Edge 的 Bookmarks 文件存储位置

在 Linux 系统中&#xff0c;Microsoft Edge 的书签&#xff08;Bookmarks&#xff09;文件存储在用户的配置目录下。具体路径通常如下&#xff1a; ~/.config/microsoft-edge/Default/Bookmarks说明&#xff1a; 路径解释&#xff1a; ~ 表示当前用户的主目录。.config 是一个…

pinia学习笔记(1.0)

首先贴出官网地址&#xff1a;开始 | Pinia pinia作为Vue3项目中常用的状态管理工具&#xff0c;正逐渐取代vuex&#xff0c;现从0到1自己搭建pinia仓库。 首先&#xff0c;安装pinia&#xff0c;使用包管理器工具&#xff08;npm,pnpm,yarn,Bun等都可以&#xff09; 安装成…

UE5运行时动态加载场景角色动画任意搭配-相机及运镜(二)

通过《MMD模型及动作一键完美导入UE5》系列文章,我们可以把外部场景、角色、动画资产导入UE5,接下来我们将实现运行时动态加载这些资产,并任意组合搭配。 1、运行时播放相机动画 1、创建1个BlueprintActor,通过这个蓝图动态创建1个LevelSequence,并Play 2、将这个Bluep…

linux基本环境配置 安装Docker RedisMysql

目录 一、安装docker 1、卸载系统之前的docker 2、安装Docker-CE 3、启动docker 4、设置docker开机自启 5、root测试docker命令 6、配置docker镜像加速 二、Docker安装Mysql 1、下载镜像文件 2、创建实例并启动 3、修改MySQL字符集 4、设置容器自启动 三、Docker安…

CTFHUB技能树之SQL——MySQL结构

开启靶场&#xff0c;打开链接&#xff1a; 先判断一下是哪种类型的SQL注入&#xff1a; 1 and 11# 正常回显 1 and 12# 回显错误&#xff0c;说明是整数型注入 判断一下字段数&#xff1a; 1 order by 2# 正常回显 1 order by 3# 回显错误&#xff0c;说明字段数是2列 知道…

Vue3嵌套导航相对路径问题

有如下的页面设计&#xff0c;页面上方第一次导航&#xff0c;两个菜单&#xff0c;首页和新闻 点击新闻&#xff0c;内容里面嵌套一个左侧和右侧&#xff0c;左侧有4条新闻&#xff0c;点击某一条新闻&#xff0c;右侧显示详情 代码如下&#xff1a; ​ File Path: d:\hello\…

【AIGC】ChatGPT提示词Prompt高效编写模式:思维链、Self-Consistency CoT与Zero-Shot CoT

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;思维链 (Chain of Thought, CoT)如何工作应用实例优势结论 &#x1f4af;一致性思维链 (Self-Consistency CoT)如何工作应用实例优势结论 &#x1f4af;零样本思维链 (Ze…

详细分析Redisson分布式锁中的renewExpiration()方法

目录 一、Redisson分布式锁的续期 整体分析 具体步骤和逻辑分析 为什么需要递归调用&#xff1f; 定时任务的生命周期&#xff1f; 一、Redisson分布式锁的续期 Redisson是一个基于Redis的Java分布式锁实现。它允许多个进程或线程之间安全地共享资源。为了实现这一点&…

51单片机数码管循环显示0~f

原理图&#xff1a; #include <reg52.h>sbit dulaP2^6;//段选信号 sbit welaP2^7;//位选信号unsigned char num;//数码管显示的数字0~funsigned char code table[]{ 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71};//定义数码管显…

web前端-----html5----用户注册

以改图为例 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>用户注册</title> </hea…