当前位置: 首页 > news >正文

C++之红黑树

目录​​​​​​​

一、红黑树的概念

1.1、红黑树的规则

1.2、红黑树如何确保最长路径不超过最短路径的二倍

1.3、红黑树的效率

二、红黑树的实现

2.1、红黑树的结构

2.2、红黑树的插入

2.2.1、红黑树插入一个值的大概过程

2.2.2、情况一:变色

2.2.3、情况二:单旋+变色

2.2.4、情况三:双旋+变色

2.2.5、插入方法的代码

2.3、红黑树的验证

2.3.1、测试

三、红黑树完整代码

RBTree.h

test.cpp


一、红黑树的概念

红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表⽰结点的颜⾊,可以是红⾊或⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束,红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍,因⽽是接近平衡的。

1.1、红黑树的规则

  1. 每个结点不是红⾊就是⿊色。
  2. 根结点是⿊⾊的。
  3. 如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的红⾊结点。
  4. 对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的⿊⾊结点。

说明:《算法导论》等书籍上补充了⼀条每个叶⼦结点(NIL)都是⿊⾊的规则。他这⾥所指的叶⼦结点不是传统的意义上的叶⼦结点,⽽是我们说的空结点(NULL),有些书籍上也把NIL叫做外部结点。NIL是为了⽅便准确的标识出所有路径,《算法导论》在后续讲解实现的细节中也忽略了NIL结点,所以我们知道⼀下这个概念即可。

1.2、红黑树如何确保最长路径不超过最短路径的二倍

  • 由规则4可知,从根到NULL结点的每条路径都有相同数量的⿊⾊结点,所以极端场景下,最短路径就就是全是⿊⾊结点的路径,假设最短路径⻓度为bh(black height)。
  • 由规则2和规则3可知,任意⼀条路径不会有连续的红⾊结点,所以极端场景下,最⻓的路径就是⼀ ⿊⼀红间隔组成,那么最⻓路径的⻓度为2*bh。
  • 综合红⿊树的4点规则⽽⾔,理论上的全⿊最短路径和⼀⿊⼀红的最⻓路径并不是在每棵红⿊树都存在的。假设任意⼀条从根到NULL结点路径的⻓度为x,那么bh <= x <= 2*bh。

1.3、红黑树的效率

假设N是红⿊树树中结点数量,h最短路径的⻓度,那么2^h - 1 <=N<2^{2*h} - 1 ,由此推出h\approx logN,也就是意味着红⿊树增删查改最坏也就是⾛最⻓路径2*logN,那么时间复杂度还是O(logN)

红⿊树的表达相对AVL树要抽象⼀些,AVL树通过⾼度差直观的控制了平衡。红⿊树通过4条规则的颜⾊约束,间接的实现了近似平衡,他们效率都是同⼀档次,但是相对⽽⾔,插⼊相同数量的结点,红⿊树的旋转次数是更少的,因为他对平衡的控制没那么严格。

二、红黑树的实现

2.1、红黑树的结构

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

2.2、红黑树的插入

2.2.1、红黑树插入一个值的大概过程

插⼊⼀个值按⼆叉搜索树规则进⾏插⼊,插⼊后我们只需要观察是否符合红⿊树的4条规则。

  • 如果是空树插⼊,新增结点是⿊⾊结点。如果是⾮空树插⼊,新增结点必须红⾊结点,因为⾮空树插⼊,新增⿊⾊结点就破坏了规则4,规则4是很难维护的。
  • ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是⿊⾊的,则没有违反任何规则,插⼊结束。
  • ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是红⾊的,则违反规则3。进⼀步分析,c是红⾊,p为红,g必为⿊,这三个颜⾊都固定了,关键的变化看u的情况,需要根据u分为以下⼏种 情况分别处理。

说明:后面的图中假设我们把新增结点标识为c(cur),c的⽗亲标识为p(parent),p的⽗亲标识为 g(grandfather),p的兄弟标识为u(uncle)

2.2.2、情况一:变色

情况:c为红,p为红,g为⿊,u存在且为红,则将p和u变⿊,g变红。在把g当做新的c,继续往上更新。

分析:因为p和u都是红⾊,g是⿊⾊,把p和u变⿊,左边⼦树路径各增加⼀个⿊⾊结点,g再变红,相当于保持g所在⼦树的⿊⾊结点的数量不变,同时解决了c和p连续红⾊结点的问题,需要继续往上更新是因为,g是红⾊,如果g的⽗亲还是红⾊,那么就还需要继续处理;如果g的⽗亲是⿊⾊,则处理结束了;如果g就是整棵树的根,再把g变回⿊⾊。

情况1只变⾊,不旋转。所以⽆论c是p的左还是右,p是g的左还是右,都是上⾯的变⾊处理⽅式。

图0:

跟AVL树类似,图0我们展⽰了⼀种具体情况,但是实际中需要这样处理的有很多种情况。

图1将以上类似的处理进⾏了抽象表达,d/e/f代表每条路径拥有hb个⿊⾊结点的⼦树,a/b代表每 条路径拥有hb-1个⿊⾊结点的根为红的⼦树,hb>=0。

图2/图3/图4,分别展⽰了hb == 0,hb == 1,hb == 2的具体情况组合分析,当hb等于2时,这⾥组合情况上百亿种,这些样例是帮助我们理解,不论情况多少种,多么复杂,处理⽅式⼀样的,变⾊再继续往上处理即可,所以我们只需要看抽象图即可。

图1:

图2:

图3:

图4:

2.2.3、情况二:单旋+变色

情况:c为红,p为红,g为⿊,u不存在或者u存在且为⿊,u不存在,则c⼀定是新增结点,u存在且为⿊,则 c⼀定不是新增,c之前是⿊⾊的,是在c的⼦树中插⼊,符合情况1,变⾊将c从⿊⾊变成红⾊,更新上来的。

分析:p必须变⿊,才能解决,连续红⾊结点的问题,u不存在或者是⿊⾊的,这⾥单纯的变⾊⽆法解决问题,需要旋转+变⾊。

结合上图:如果p是g的左,c是p的左,那么以g为旋转点进⾏右单旋,再把p变⿊,g变红即可。p变成颗这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为p的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

结合上图:如果p是g的右,c是p的右,那么以g为旋转点进⾏左单旋,再把p变⿊,g变红即可。p变成课这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为p的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

2.2.4、情况三:双旋+变色

情况:c为红,p为红,g为⿊,u不存在或者u存在且为⿊,u不存在,则c⼀定是新增结点,u存在且为⿊,则 c⼀定不是新增,c之前是⿊⾊的,是在c的⼦树中插⼊,符合情况1,变⾊将c从⿊⾊变成红⾊,更新上来的。

分析:p必须变⿊,才能解决,连续红⾊结点的问题,u不存在或者是⿊⾊的,这⾥单纯的变⾊⽆法解决问题,需要旋转+变⾊。

结合上图:如果p是g的左,c是p的右,那么先以p为旋转点进⾏左单旋,再以g为旋转点进⾏右单旋,再把c变⿊,g变红即可。c变成课这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为c的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

结合上图:如果p是g的右,c是p的左,那么先以p为旋转点进⾏右单旋,再以g为旋转点进⾏左单旋,再把c变 ⿊,g变红即可。c变成课这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为c的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

2.2.5、插入方法的代码

这里使用的旋转方法是基于AVL树那篇博客中的旋转方法,将平衡因子的部分去掉就可以。

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;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{return false;}}cur = new Node(kv);// 新增节点。颜色红色给红色cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left) //叔叔在右边{Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){//叔叔存在且为红 变色处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{//叔叔存在且为黑或叔叔不存在 旋转+变色if (cur == parent->_left){//		g//   p     u// c   //单旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED; }else{//		g//  p	    u//    c //双旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else  //叔叔在左边{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){//叔叔存在且为红 变色处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{//叔叔存在且为黑或叔叔不存在 旋转+变色if (cur == parent->_right){//		g//   u     p//           c//单旋RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//  u	    p//        c//双旋RotateR(parent);RotateL (grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

2.3、红黑树的验证

这⾥获取最⻓路径和最短路径,检查最⻓路径不超过最短路径的2倍是不可⾏的,因为就算满⾜这个条件,红⿊树也可能颜⾊不满⾜规则,当前暂时没出问题,后续继续插⼊还是会出问题的。所以我们还 是去检查4点规则,满⾜这4点规则,⼀定能保证最⻓路径不超过最短路径的2倍。

  • 规则1枚举颜⾊类型,天然实现保证了颜⾊不是⿊⾊就是红⾊。
  • 规则2直接检查根即可。
  • 规则3前序遍历检查,遇到红⾊结点查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲的颜⾊就⽅便多了。
  • 规则4前序遍历,遍历过程中⽤形参记录跟到当前结点的blackNum(⿊⾊结点数量),前序遍历遇到⿊⾊结点就++blackNum,⾛到空就计算出了⼀条路径的⿊⾊结点数量。再任意⼀条路径⿊⾊结点数量作为参考值,依次⽐较即可。

代码:

public:	
bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;// 参考值 int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);}private:bool Check(Node* root, int blackNum, const int refNum){if (root == nullptr){// 前序遍历走到空时,意味着⼀条路径走完了 //cout << blackNum << endl;if (refNum != blackNum){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);}

2.3.1、测试

代码:

void TestRBTree2()
{const int N = 10000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);}size_t begin2 = clock();RBTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;cout << "isBalance:" << t.IsBalance() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();//查找确定在的值 //for (auto e : v)//{//t.Find(e);//}//查找随机值 for (size_t i = 0; i < N; i++){t.Find((rand() + i));}size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}

效果:

三、红黑树完整代码

RBTree.h

#pragma once#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:RBTree() = default;RBTree(const RBTree<K, V>& t){_root = Copy(t._root);}RBTree<K, V>& operator=(RBTree<K, V> t){swap(_root, t._root);return *this;}~RBTree(){Destroy(_root);_root = nullptr;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;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{return false;}}cur = new Node(kv);// 新增节点。颜色红色给红色cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left) //叔叔在右边{Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){//叔叔存在且为红 变色处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{//叔叔存在且为黑或叔叔不存在 旋转+变色if (cur == parent->_left){//		g//   p     u// c   //单旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED; }else{//		g//  p	    u//    c //双旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else  //叔叔在左边{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){//叔叔存在且为红 变色处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{//叔叔存在且为黑或叔叔不存在 旋转+变色if (cur == parent->_right){//		g//   u     p//           c//单旋RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//  u	    p//        c//双旋RotateR(parent);RotateL (grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void InOrder(){_InOrder(_root);cout << endl;}int Height(){return _Height(_root);}int Size(){return _Size(_root);}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;// 参考值 int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);}private:bool Check(Node* root, int blackNum, const int refNum){if (root == nullptr){// 前序遍历走到空时,意味着⼀条路径走完了 //cout << blackNum << endl;if (refNum != blackNum){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);}int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}void  RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void RotateRL(Node* parent){RotateR(parent->_right);RotateL(parent);}void RotateLR(Node* parent){RotateL(parent->_left);RotateR(parent);}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_kv);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}private:Node* _root = nullptr;
};void TestRBTree1()
{RBTree<int, int> t;//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){/*if (e == 9){int i = 0;}*/t.Insert({ e, e });//cout << e << "->" << t.IsBalanceTree() << endl;}t.InOrder();cout << t.IsBalance() << endl;
}void TestRBTree2()
{const int N = 10000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);}size_t begin2 = clock();RBTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;cout << "isBalance:" << t.IsBalance() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();//查找确定在的值 //for (auto e : v)//{//t.Find(e);//}//查找随机值 for (size_t i = 0; i < N; i++){t.Find((rand() + i));}size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}

test.cpp

#include"RBTree.h"int main()
{//TestRBTree1();TestRBTree2();return 0;
}

相关文章:

C++之红黑树

目录​​​​​​​ 一、红黑树的概念 1.1、红黑树的规则 1.2、红黑树如何确保最长路径不超过最短路径的二倍 1.3、红黑树的效率 二、红黑树的实现 2.1、红黑树的结构 2.2、红黑树的插入 2.2.1、红黑树插入一个值的大概过程 2.2.2、情况一&#xff1a;变色 2.2.3、情…...

各个语言对不同数据结构的叫法

一、基础数据结构对比 数组&#xff08;Array&#xff09;‌ C/C‌&#xff1a;固定大小数组&#xff08;int arr&#xff09;&#xff0c;动态数组通过vector&#xff08;C&#xff09;实现 ‌ Java‌&#xff1a;固定数组&#xff08;int[]&#xff09;&#xff0c;动态数组…...

蓝桥杯 web 水果拼盘 (css3)

做题步骤&#xff1a; 看结构&#xff1a;html 、css 、f12 分析: f12 查看元素&#xff0c;你会发现水果的高度刚好和拼盘的高度一样&#xff0c;每一种水果的盘子刚好把页面填满了&#xff0c;所以咱们就只要让元素竖着排列&#xff0c;加上是竖着&#xff0c;排不下的换行…...

算法专题(八):分治-归并排序

目录 一、排序数组 1.1 题目 2.2 思路 2.3 代码实现 二、LCR 170. 交易逆序对的总数 &#xff08;数组中的逆序对&#xff09; 2.1 题目 2.2 思路 方法一&#xff1a;快速统计出某个数前面有多少个数比它大 方法二&#xff1a;快速统计出某个数后面有多少个数比它小 …...

51单片机使用定时器实现LCD1602的时间显示(STC89C52RC)

本文前半部分直接给出实现&#xff08;注意进位问题是秒->分->小时&#xff0c;用 if 嵌套即可实现&#xff09;&#xff0c;后半部分讲解定时器和中断系统。 效果展示&#xff1a; LCD1602电路图&#xff1a; 项目结构&#xff1a; 代码实现&#xff1a; main.c #…...

微软2025年AI技术深度解析:从多模态大模型到企业级代理服务

微软2025年AI技术深度解析&#xff1a;从多模态大模型到企业级代理服务 一、微软AI技术全景概览 在2025年的AI领域&#xff0c;微软通过Azure AI Foundry、多模态大模型、企业级AI代理三大核心技术&#xff0c;构建了覆盖开发、部署、应用全流程的AI生态体系。根据最新财报数…...

24 设计模式总结

设计模式分类&#xff08;意图&#xff09; • 创建型模式&#xff1a;创建对象的机制&#xff0c;从所需要实例化的对象中解耦。 • 结构型模式&#xff1a;将对象或类组装到更大的结构中。 • 行为型模式&#xff1a;负责对象间的交互和分配职责。分类的目的是为了更抽象的了…...

【ARTS】2873.有序三元组中的最大值!

前言 仅做学习使用&#xff0c;侵删 什么是ARTS&#xff1f; 算法(Algorithm): 每周至少一道LeetCode算法题&#xff0c;加强编程训练和算法学习 阅读(Review)&#xff1a; 阅读并点评至少一篇英文技术文章&#xff0c;提高英文水平 技巧 (Tip)&#xff1a;学习至少一个技…...

Mysql进阶

目录 一.Mysql架构 1.连接层 2.服务层 3.引擎层 4.物理文件存储层 二.Mysql引擎 1.InnoDB 2.MyISAM 三.索引 1.什么是索引 2.为什么要有索引 3.索引的原理 4.索引优势 5.索引劣势 6.索引分类 主键索引 唯一索引 单值索引 组合索引&#xff08;复合索引&#…...

探秘JVM内部

在我们编写Java代码&#xff0c;点击运行后&#xff0c;会发生什么事呢&#xff1f; 首先&#xff0c;Java源代码会经过Java编译器将其编译成字节码&#xff0c;放在.class文件中 然后这些字节码文件就会被加载到jvm中&#xff0c;然后jvm会读取这些文件&#xff0c;调用相关…...

c语言学习12天

c语言的宏定义&#xff1a;宏定义单纯的文本替换不会检查语法是否合法 #include #pragma 以及开头的#都属于预处理指令 预处理指令&#xff1a;在gcc编译套件中的cpp预处理器对程序进行编译之前所做的一些动作&#xff0c;如#include预处理指令就是在程序编译之前有预处理器…...

公司内网部署离线deepseek本地模型实战

企业内部可能有些数据比较敏感&#xff0c;不能连接互联网。deepseek来提高工作效率&#xff0c;这个时候你可以利用ollama在内网本地部署来实现。 本式样是先在自己电脑上用虚拟机部署好&#xff0c;再用U盘把虚拟机文件复制到内网去。 一、使用VMware新建WIN2022虚拟机 &a…...

rocketmq中的延迟队列使用详解

RocketMQ的延迟队列通过预设的延迟等级实现消息的定时投递&#xff0c;适用于订单超时、定时通知等高并发场景。以下是其核心原理、使用方式及优化策略的详细解析&#xff1a; 一、实现原理 延迟等级机制 RocketMQ默认提供18个固定延迟等级&#xff08;1s、5s、10s、30s、1m、2…...

VB.NET Asp.Net Core模板WebAPI应用-宝塔面板Linux系统通过Docker部署

宝塔面板支持在Linux系统上部署Docker容器吗&#xff1f; 如何在宝塔面板上通过Docker部署VB.NET应用&#xff1f; Docker容器中的VB.NET Asp.Net Core WebAPI应用如何配置&#xff1f; 一,首先,创建一个ASP.NET Core测试项目 1.1 打开VS2019/2022,创建一个.NTE6 Core控制台应…...

4985 蜗牛

4985 蜗牛 ⭐️难度&#xff1a;中等 ⭐️考点&#xff1a;2023、省赛、动态规划 &#x1f4d6; &#x1f4da; import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner sc new Sc…...

springboot多模块工程打包部署运行

1、问题概述? 基于实际项目打包过程,各种配置面面俱到,已配置的可跳过。 本文以打包jar包为模板进行操作,部署方便。 在实际的开发中,项目的模块可能较多,如果都放在一个项目的目录中,势必会造成项目包中的文件冗余,难以管理,这个时候就需要使用多模块管理项目。 …...

吴恩达深度学习复盘(8)神经网络中激活函数的建模

激活函数的建模原理 到目前为止&#xff0c;在隐藏层等一直使用激活函数&#xff0c;最初通过逻辑回归建立新网络&#xff0c;组合多个逻辑回归单元。这表明激活函数在神经网络构建中一直存在&#xff0c;且最初的网络构建方式与逻辑回归相关。实际上&#xff0c;激活函数的种类…...

1-linux的基础知识

一.linux的文件系统结构 windows文件系统 微软windows系统将硬盘上的几个分区&#xff0c;用A: B: C: D:等符号标识。存取文件时一定要清楚放在那个磁盘的那个目录下。 linux文件系统 linux文件系统的组织模式犹如一颗倒置的树&#xff0c;这与windows文件系统有很大的差别…...

docker 常用命令

文章目录 一、帮助启动类命令启动docker停止docker重启docker查看docker状态开机自启查看docker概要信息 二、镜像命令列出本地主机上的镜像搜索镜像拉取镜像查看镜像所占空间删除镜像 三、容器命令新建运行容器交互式启动容器守护进程式启动容器列出当前所有的容器进入容器之后…...

使用docker搭建redis镜像时云服务器无法访问到国外的docker官网时如何解决

下载redis镜像 docker redis:版本号 此时截图中无法访问到国外的docker官网 解决方案&#xff1a; 通过更换镜像源来正常下载redis镜像 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF {"registry-mirrors": ["https://docker.1…...

基于Python的人脸识别校园考勤系统

【Python】基于Python的人脸识别校园考勤系统 &#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 &#x1f31f; 该系统主要分为前端和后端两个部分&#xff0c;前端&#x1f440;负责人脸采集、人…...

微信小程序学习实录11:startLocationUpdateBackground:fail auth deny

startLocationUpdateBackground:fail auth deny 表明小程序在尝试开启后台位置更新时&#xff0c;用户授权被拒绝。以下是可能的原因及解决方法&#xff1a; 原因分析 缺少必要的用户授权&#xff1a; 使用 wx.startLocationUpdateBackground 接口需要用户授予 scope.userLo…...

DAPP实战篇:规划下我们的开发线路

前言 在DApp实战篇&#xff1a;先用前端起个项目一文中我们起了一个前端项目&#xff0c;在后续开发中笔者将带领大家一步步完成这个DAPP&#xff0c;为了方便后续讲解&#xff0c;本篇将完整说明后续我们要进行的开发和思路。 主打前端 实际上一个完整的DAPP是由前端和智能…...

docker配置redis容器时配置文件docker-compose.yml示例

1.配置数据节点&#xff08;主从节点&#xff09; version: 3.7 services:master:image: redis:5.0.9container_name: redis-masterrestart: alwayscommand: redis-server --appendonly yesports:- 6379:6379slave1:image: redis:5.0.9container_name: redis-slave1restart: a…...

清晰易懂的 Jenkins 安装与核心使用教程

Jenkins 是业界领先的开源自动化服务器&#xff0c;用于实现持续集成与持续交付&#xff08;CI/CD&#xff09;。本教程将覆盖 安装部署、核心功能配置、避坑指南&#xff0c;助你快速掌握企业级自动化流水线搭建&#xff01; 一、Jenkins 安装&#xff08;全平台指南&#xff…...

anomalib—2—输入图像大小调整

三个地方 第一&#xff1a;在定义model时&#xff0c;要在pre_processor里面去定义一个前处理&#xff0c;前处理就一个功能&#xff0c;定义图像的大小 pre_processor0 Patchcore.configure_pre_processor( image_size (128, 128)) model Patchcore( backbone"wide_r…...

小型园区组网图

1. 在小型园区中&#xff0c;S5735-L-V2通常部署在网络的接入层&#xff0c;S8700-4通常部署在网络的核心&#xff0c;出口路由器一般选用AR系列路由器。 2. 接入交换机与核心交换机通过Eth-Trunk组网保证可靠性。 3. 每个部门业务划分到一个VLAN中&#xff0c;部门间的业务在C…...

编程哲学——TCP可靠传输

TCP TCP可靠传输 TCP的可靠传输表现在 &#xff08;1&#xff09;建立连接时三次握手&#xff0c;四次挥手 有点像是这样对话&#xff1a; ”我们开始对话吧“ ”收到“ ”好的&#xff0c;我收到你收到了“ &#xff08;2&#xff09;数据传输时ACK应答和超时重传 ”我们去吃…...

2024华为OD机试真题-任务最优调度(C++/Java/Python)-E卷-200分

2024华为OD机试最新E卷题库-(D卷+E卷)-(JAVA、Python、C++) 目录 题目描述 输入描述 输出描述 用例1 考点 题目解析 代码 c++ java python 题目描述 给定一个正整数数组表示待系统执行的任务列表,数组的每一个元素代表一个任务,元素的值表示该任务的类型。请计算执…...

蓝桥杯 2023省B 飞机降落 dfs

传送门 P9241 [蓝桥杯 2023 省 B] 飞机降落 - 洛谷 n<10&#xff0c;考虑dfs&#xff0c;只有当 当前飞机的到达时刻盘旋时间 < 上一个飞机降落的时刻 时&#xff0c;当前飞机才能降落 const int N 1e3 10;int n; struct Node {LL t,d,l; }a[N];bool st[N];bool dfs(in…...

Mybatis--动态SQL

动态SQL是MyBatis的重要特征之一&#xff0c;能够完成不同条件下的SQL拼接&#xff0c;参考文档&#xff1a;动态 SQL_MyBatis中文网 一、<if>标签 该标签主要适用的情况为实现必填字段和非必填字段&#xff1a; 例如下面的例子就是将用户表中的性别设置成了非必填字段…...

计算机视觉中的基于网格的卷绕算法全解析

大家好呀&#xff5e;今天给大家带来一个超级实用且有趣的计算机视觉技巧&#xff1a;基于网格的卷绕算法&#xff08;Grid Warp Algorithm&#xff09;&#xff01;如果你对图像变形、动画制作感兴趣&#xff0c;那一定不要错过这篇文章哦&#xff01;话不多说&#xff0c;直接…...

xv6 文件系统

Buffer Cache buffer Cache 结构体 bcache 存放了 NBUF 个 buf 框&#xff0c;每个框对应 disk 上某一个 block。从初始化函数 binit中可以看出&#xff0c;bcache 是一个循环双向链表。通过双链表组织这些 buf&#xff0c;以近似 LRU 的策略管理&#xff0c;大概如下图。 st…...

Python Cookbook-5.5 根据内嵌的数字将字符串排序

任务 你想将一个字符串列表进行排序&#xff0c;这些字符串都含有数字的子串(比如一系列邮寄地址)。举个例子&#xff0c;“foo2.txt”应该出现在“foo10.txt”之前。然而&#xff0c;Python 默认的字符串比较是基于字母顺序的&#xff0c;所以默认情况下&#xff0c;“foo10.…...

EMC内参二(1-45页)学习【技术进阶】

EMC设计介入产品设计时间越早&#xff0c;成本越低。 微带线和带状线的区别&#xff1a; 微带线是PCB外层的走线&#xff0c;带状线是结余两个完整参考平面&#xff08;电源层和地层&#xff09;之间的走线。 天线效应&#xff1a; PCB上面任何悬空的金属都会积累电荷&…...

Ansible(7)——管理机密

目录 一、Ansible Vault &#xff1a; 二、ansible-vault 命令行工具&#xff1a; 1、创建加密文件&#xff1a; 2、查看加密文件&#xff1a; 3、编辑现有加密文件&#xff1a; 4、加密现有文件&#xff1a; 5、解密现有文件&#xff1a; 6、更改加密文件的密码&#…...

通俗地讲述DDD的设计

通俗地讲述DDD的设计 前言为什么要使用DDDDDD架构分层重构实践关键问题解决方案通过​​领域事件机制​​解耦服务依赖&#xff1a;防止逻辑下沉 领域划分电商场景下的领域划分 结语完结撒花&#xff0c;如有需要收藏的看官&#xff0c;顺便也用发财的小手点点赞哈&#xff0c;…...

【学Rust写CAD】34 精确 Alpha 混合函数(argb.rs补充方法)

源码 #[inline]pub fn over_exact(self, dst: Argb) -> Argb {let a 255 - self.alpha32();let t dst.rb() * a 0x80_00_80;let mut rb (t ((t >> 8) & Argb::MASK)) >> 8;rb & Argb::MASK;rb self.rb();// saturaterb | 0x1000100 - ((rb >&…...

10种电阻综合对比——《器件手册--电阻》

二、电阻 前言 10种电阻对比数据表 电阻类型 原理 特点 应用 贴片电阻 贴片电阻是表面贴装元件&#xff0c;通过将电阻体直接贴在电路板上实现电路连接 体积小、重量轻&#xff0c;适合高密度电路板&#xff1b;精度高、稳定性好&#xff0c;便于自动化生产 广泛应用于…...

SpringCloud入门及创建分布式项目

1、了解微服务 1.1 什么是微服务 微服务是一种架构风格一个应用拆分为一组小型服务每个服务运行在自己的进程内&#xff0c;也就是可独立部署和升级服务之间使用轻量级HTTP交互服务围绕业务功能拆分可以由全自动部署机制独立部署去中心化&#xff0c;服务自治。服务可以使用不同…...

xv6启动过程

entry,S -> start.c -> main.c -> proc.c中的 userinit 函数 -> initcode.S -> init.c entry.S // entry.S# qemu -kernel loads the kernel at 0x80000000# and causes each CPU to jump there.# kernel.ld causes the following code to# be placed at 0x800…...

【秣厉科技】LabVIEW工具包——OpenCV 教程(18):highgui 模块

文章目录 前言highgui 模块总结 前言 需要下载安装OpenCV工具包的朋友&#xff0c;请前往 此处 &#xff1b;系统要求&#xff1a;Windows系统&#xff0c;LabVIEW>2018&#xff0c;兼容32位和64位。 highgui 模块 尽量别用&#xff0c;要不我删了吧&#xff1f; LabVIEW…...

基于OPENCV的图像透视矫正

这段代码的主要功能是对输入的图像进行透视矫正。它会读取一张图像&#xff0c;检测图像中最大的四边形轮廓&#xff0c;然后对该四边形区域进行透视变换&#xff0c;将其矫正为正视图&#xff0c;最后保存矫正后的图像。 模块导入说明 python import cv2 import numpy as n…...

数据结构----------顺序查找,折半查找和分块查找(java实现)

import java.util.Arrays;//顺序查找法 public class Main {public static void main(String[] args) {//查找表int[] arr {4, 3, 5, 1, 2};System.out.print("5在数组中的索引:");System.out.println(SearchSeq(arr, 5));Arrays.sort(arr);System.out.print("…...

整数编码 - 华为OD统一考试(A卷、JavaScript)

题目描述 实现一种整数编码方法&#xff0c;使得待编码的数字越小&#xff0c;编码后所占用的字节数越小。 编码规则如下: 编码时7位一组&#xff0c;每个字节的低7位用于存储待编码数字的补码。字节的最高位表示后续是否还有字节&#xff0c;置1表示后面还有更多的字节&…...

CompletableFuture:整合、超时、完成事件与批量处理

引言 在异步编程实践中&#xff0c;我们不仅需要处理单个任务的执行流程&#xff0c;更需要应对多个异步任务之间的复杂交互。本文将通过实际案例解析以下核心功能&#xff1a; 双任务整合&#xff1a;合并两个独立任务的结果高效超时控制&#xff1a;防止异步操作无限等待完…...

【LeetCode 热题100】45:跳跃游戏 II(详细解析)(Go语言版)

&#x1f680; 力扣 45&#xff1a;跳跃游戏 II&#xff08;全解法详解&#xff09; &#x1f4cc; 题目描述 给你一个非负整数数组 nums&#xff0c;表示你最初位于数组的第一个位置。 数组中的每个元素表示你在该位置可以跳跃的最大长度。 你的目标是使用 最少的跳跃次数 到…...

【C/C++】滑动谜题(leetcode T773)

核心考点&#xff1a;广度优先搜索 (BFS)、哈希表、字符串、状态转移 题目描述&#xff1a; 在一个 2 x 3 的板上&#xff08;board&#xff09;有 5 块砖瓦&#xff0c;用数字 1~5 来表示, 以及一块空缺用 0 来表示。一次 移动 定义为选择 0 与一个相邻的数字&#xff08;上…...

python用x08覆盖上一次输出来模拟控制台等待效果,pycharm运行sys.stdout.write在控制台无打印的解决方法

一个多进程程序&#xff0c;主进程阻塞&#xff0c;子进程不断打印等待效果直到主进程结束&#xff0c;原理是\x08在ascii中表示退格键&#xff0c;理解为打印完后立马删掉打印下一个内容。 import sys, time import multiprocessing DELAY 0.1 DISPLAY [ |, /, -, \\ …...

【嵌入式开发】使用Linux系统调用编程练习

一、进程和线程的概念及基础用法 在Linux系统中&#xff0c;进程&#xff08;Process&#xff09;和线程&#xff08;Thread&#xff09;是操作系统进行任务调度的基本单位&#xff0c;它们既有联系又有区别。 1.1 进程和线程介绍 1.1.1 进程&#xff08;Process&#xff09…...