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

RBTree(红黑树)的介绍和实现

   欢迎来到杀马特的主页:羑悻的小杀马特.-CSDN博客

目录

​编辑

一·红黑树介绍:

1.1红黑树概念:

1.2红黑树遵循的原则:

 1.3红黑树效率分析:

二.红黑树的实现:

2.1红黑树结构:

2.2红黑树节点的插入:

2.2.1 顺序性插入:

2.2.2.1变色(叔叔节点为红色):

 2.2.2.2变色(叔叔节点为红色)(简单总结版):

2.2.3.1 左单旋+变色(叔叔节点为黑色或者不存在):

 2.2.3.2左单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

2.2.4.1 右单旋+变色(叔叔节点为黑色或者不存在):

2.2.4.2 右单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

2.2.5.1 左右双旋+变色(叔叔节点为黑色或者不存在):

2.2.5.2 左右双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

2.2.6.1右左双旋+变色(叔叔节点为黑色或者不存在):

 2.2.6.2右左双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

 2.3红黑树的查找:​编辑

2.4红黑树的验证: 

2.5红黑树的删除:

三·代码总结: 

四·红黑树创建总结(仅个人理解): 


一·红黑树介绍:

1.1红黑树概念:

首先可以把它理解成一颗二叉搜索树,但是它的节点会有颜色不是红就是黑,可以这么理解:就是avl树把平衡因子去掉并改成颜色再加以修改,但是平衡还是有点差别,高度可能会差别大于2,因此只能勉勉强强这麽说。这样可以保证如果存在最短路径必然都是黑节点,如果存在最长路径必然一般黑一般红,其他路径就位于它们之间了,它根据特性也可以默认成平衡的了,这里也要注意空节点默认为黑色(这里也可有头结点也可无,如果有的话就是头结点为黑色,然后它的parent指针指向根节点,左指针指向树最左边孩子,右指针指向树最右边的孩子)。

1.2红黑树遵循的原则:

简称红黑树四大原则:

①它的结点不是红⾊就是⿊⾊ 。

②根结点是⿊⾊的。

③如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的 红⾊结点,故红红节点不能连续出现。

 ④对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的⿊⾊结点。

 1.3红黑树效率分析:

这里如果我们假设树的最短高度是h,而最长高度就是2h(假设存在的话):为什么?:这里若果是最短(由于黑色节点相同)故只需要它无红色几点即全黑设高度为h,那么最长的只能是红黑交错出现(红色不能相连)故就是2h了。 那么我们证明一下它的效率范围:

这里最短路路径就是18 10 而最长就是 18 30 40 50,我们以最坏情况考虑(因为在最短和最长之间的点是怎样分布不清楚故这样)那么最好就是2^h-1,最长是2^2h-1,也就是节点是这些,设节点为n解答时间复杂度=logn与2*logn之间,故都可以近似成o(logn)。

二.红黑树的实现:

2.1红黑树结构:

这里和avl树大致相同就是把平衡因子等换成了颜色的枚举类型了。

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 {using Node = RBTreeNode<K, V>;
public:private:
Node* _root=nullptr;};

2.2红黑树节点的插入:

这里剧透一下:那么插入过程大概是怎么样的呢?首先我们先找到了大小关系合适的节点;然后如果我们插入的是黑色就破坏了黑色相等的规则,故插入的节点一定是红色,那么就可以推出它的父亲是红或者黑,如果是黑色那么可以直接成功插入了,但是如果是红色的话,爷爷节点一定是黑色,此时关键就要看叔叔节点了:这时候分为三种情况为黑,为红,不存在(为黑),故这里还可以归总成两种:1·叔叔节点为红色,2·叔叔节点为黑色。----->"调整的环节又叫"关键看叔叔"

下面说一下这个插入的思路:就是当我们插入后父亲是黑色就退出,如果是红色:那么就继续向上调整(这里可以直到如果父亲为红肯定存在爷爷即为黑,但是爷爷的父亲不一定存在,那么如果下一次向上调整爷爷变成了孩子,那么可能爷爷就是根节点,但是会被变红,故需要最后变黑),此时就要看叔叔了。根据那两种情况进行旋转或者不旋转。 

代码思路:首先分为两大方向分别是父亲是爷爷的左孩子与右孩子,此时叔叔就相反了,然后再分叔叔为黑或者红(直接变色),为黑:则要分孩子插入的是父亲的左还是右进行对应的左 右单旋和左右 右左双旋 。

2.2.1 顺序性插入:

就是按照比原节点值小就向左走,大就向右走,然后走到空,判断插入的是左还是右进行插入即可。

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;

2.2.2.1变色(叔叔节点为红色):

叔叔节点为红色的情况(这里首先要确定叔叔是爷爷的左还是右(父亲,叔叔的连接如何)):

假设插入的是c这个节点,故此时父亲是红色,故要变色(由于要保证每条路黑色节点个数不变,故我们可以先把父亲和叔叔节点变成黑色,爷爷变成红色就行了-->这样可以保证黑色节点相当于没变化。),接着就是向上调整(让爷爷节点变成孩子)看看父亲节点颜色,红色:就看叔叔颜色,进行相关变化黑色:退出。

代码实现:

if (uncle && uncle->_col == RED) {parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理:cur = grandfather;parent = cur->_parent;
}

 2.2.2.2变色(叔叔节点为红色)(简单总结版):

判断父亲叔叔方位,叔叔为红,父亲叔叔变黑,爷爷变红继续向上走。

2.2.3.1 左单旋+变色(叔叔节点为黑色或者不存在):

注:这里是由于要进行不同方向的那四种旋转我们才分孩子连接的是父亲的左还是右。

我们首先要想什么时候会左单旋然后变色呢?(首先保持好它的平衡性,故还要旋转变平衡,最后保证节点颜色规则。)  因此左单旋肯定是右边高了,而根据标题就可以知道此时叔叔节点是黑色,但是它是不是第一次插入就黑色了呢?肯定不可能,下面看一张图解释一下:

但是当叔叔不存在时候(虽然也是黑色)但是它只能是新插入的节点而不是向上调整才会出现:

这样我们就明白了把。因此接下来如果要进行左旋故父亲肯定是爷爷的右,孩子也是父亲的右(无论叔叔存在还是存在为黑都一样当黑色处理进行左旋。) 

故我们要先保持它的平衡(右边高了)(利用左旋(对g)):

①首先是叔叔为空(黑):

这样才能保持平衡后,还符合颜色规则。 

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

这样我们左单旋的所有情况就完了。

代码实现:

//又分两种情况:孩子插入的是左还是右:
if (cur == parent->_right) {RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;
}

 2.2.3.2左单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷右,孩子连接父亲右,叔叔存在且黑色(不存在),进行左旋,爷爷变红,父亲变黑。

2.2.4.1 右单旋+变色(叔叔节点为黑色或者不存在):

注:这里是由于要进行不同方向的那四种旋转我们才分孩子连接的是父亲的左还是右。

这里其实就和单旋一样(叔叔存在黑色或者不存在),但是此时就是父亲连接爷爷的左,孩子连接父亲左了。

下面就分情况画图简单看一下吧:

①首先是叔叔为空(黑):

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

代码实现:

//又分两种情况:孩子插入的是左还是右:
if (cur == parent->_left) {RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;
}

2.2.4.2 右单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷左,孩子连接父亲左,叔叔存在且黑色(不存在),进行旋右,爷爷变红,父亲变黑。

2.2.5.1 左右双旋+变色(叔叔节点为黑色或者不存在):

注:这里是由于要进行不同方向的那四种旋转我们才分孩子连接的是父亲的左还是右。

首先我们要知道为什么会出现左右双旋?个人理解:它是右旋样子下孩子插入在父亲右边才会形成的,故此时我们还要分叔叔节点为存在为黑色以及不存在情况进行画图分析。

先说一下它的情况吧:父亲连节爷爷的左边,孩子连接父亲的右边。

①首先是叔叔为空(黑):

对于叔叔是空只能是一开始第一次插入就这样出现的(上面也分析了),下面展示一下它插入并完成旋转和变色的图:

此时我们可以看出进行双旋后把孩子变黑,爷爷变红了。 

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

还是老样子叔叔为黑,则孩子节点一定是向上调整由黑变红来的,展示一下:

同理也是孩子变黑,爷爷变红,才能符合既平衡又符合黑色相同规则。 

代码实现:

RotateLR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;

2.2.5.2 左右双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷的左,孩子连接父亲的右,叔叔为黑或者不存在,此时爷爷变红,孩子变黑。

2.2.6.1右左双旋+变色(叔叔节点为黑色或者不存在):

其实此时的右左双旋和上面的左右双旋相似,只不过是父亲连接了爷爷的右,而孩子连接了父亲的左而已,下面就不过多重复了,画图看一下:

①首先是叔叔为空(黑):

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

代码实现:

else {RotateRL(grandfather);cur->_col = BLACK;grandfather->_col = RED;
}

 2.2.6.2右左双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷的右,孩子连接父亲的左,叔叔为黑或者不存在,此时爷爷变红,孩子变黑。

 2.3红黑树的查找:

这里遵循avl的查找只查找pair的first(根据)而不管颜色故直接用它的查找就行。

代码实现:

 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;}

2.4红黑树的验证: 

这时候我们就要根据红黑树的四大原则了,但是这里我们只用到了三个:

①根节点为空就是不为空必须是黑色。

②每个节点(这里认为的是根节点起,到空的每条支路的黑色节点数相同)。

③红色节点的孩子一定是黑色节点,但是这里如果这么找,还要判断孩子存不存在然后看颜色,因此我们可以倒着来即找到一个节点是红色,则它的父亲不能是红色(即红色节点不相连)。

黑色节点相同的话,我们可以先往最左或者最右遍历至空,找到一条路径的黑色节点个数作为标准,然后利用递归到空记录blackcount与它比较发现不等就false,红色不相连:遍历途中,发现红色就看父亲颜色,如果为红色就false。

代码展示:

 bool is_rbtree() {int count = 0;if (_root == nullptr) return true;if (_root->_col == RED) return false;//根节点存在就为黑Node* cur = _root;while (cur) {if (cur->_col == BLACK) count++;cur = cur->_left;}return check(_root, count, 0);}bool check(Node* root, int reference, int blackcount) {//查找树中要满足黑色相同以及红的节点只能连两个黑的(转化成红的节点父亲不能是红的):这里只有都满足才返回true,故可以判断错就返falseif (root == nullptr) {//检查每条支路黑色节点数量相同if (blackcount != reference) {cout << "发现黑色节点不等的支路" << endl;return false;}else return true;}if (root->_col == RED && root->_parent->_col == RED) {cout << "发现连续的红色节点" << endl;//不能有连续的红色节点return false;}if (root->_col == BLACK) blackcount++;return check(root->_left, reference, blackcount) && check(root->_right, reference, blackcount);
}

2.5红黑树的删除:

这里和上次的avl树一样采取的是替换制删除,可以根据上篇avl树的删除来仿写,但是就是把平衡因子换成了红黑色的颜色而已。 

三·代码总结: 

#pragma once
#include<iostream>
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 {using Node = RBTreeNode<K, V>;
public:bool insert(const pair<K, V>& kv) {//先按照avl树插入节点,然后最后判断是否旋转来修改颜色//第一次插入要保证根节点为黑色: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;//爷爷节点一定存在(当父亲节点为红)一定为黑:要么继续调整要么为根节点最后改黑//下面分两种大情况分别是父亲为爷爷的left和right: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) {RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else {RotateLR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}}}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) {RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else {RotateRL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}}}}_root->_col = BLACK;//如果到头爷爷原来是黑的被改成了红且是根节点故改回黑return true;}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 is_rbtree() {int count = 0;if (_root == nullptr) return true;if (_root->_col == RED) return false;//根节点存在就为黑Node* cur = _root;while (cur) {if (cur->_col == BLACK) count++;cur = cur->_left;}return check(_root, count, 0);}int treeheight() {return _treeheight(_root);}int size() {return _size(_root);}void InOrder(){_InOrder(_root);cout << endl;}private:void RotateL(Node* parent) {//左旋Node* subr = parent->_right;Node* subrl = subr->_left;//处理sublr:parent->_right = subrl;if (subrl) subrl->_parent = parent;//sublr为空不能访问Node* pp = parent->_parent;//保存parent的父节点指针//调节新旧“parent”位置:subr->_left = parent;parent->_parent = subr;if (pp == nullptr) {_root = subr;subr->_parent = nullptr;}else {if (parent == pp->_left) pp->_left = subr;else pp->_right = subr;subr->_parent = pp;}}void RotateR(Node* parent) {//右旋Node* subl = parent->_left;Node* sublr = subl->_right;parent->_left = sublr;if (sublr) sublr->_parent = parent;Node* pp = parent->_parent;subl->_right = parent;parent->_parent = subl;if (pp == nullptr) {_root = subl;subl->_parent = nullptr;}else {if (parent == pp->_left) pp->_left = subl;else pp->_right = subl;subl->_parent = pp;}}void	RotateLR(Node* parent) {//左右双旋Node* subl = parent->_left;Node* sublr = subl->_right;RotateL(subl);RotateR(parent);}void RotateRL(Node* parent) {//右左双旋Node* subr = parent->_right;Node* subrl = subr->_left;RotateR(subr);RotateL(parent);}bool check(Node* root, int reference, int blackcount) {//查找树中要满足黑色相同以及红的节点只能连两个黑的(转化成红的节点父亲不能是红的):这里只有都满足才返回true,故可以判断错就返falseif (root == nullptr) {//检查每条支路黑色节点数量相同if (blackcount != reference) {cout << "发现黑色节点不等的支路" << endl;return false;}else return true;}if (root->_col == RED && root->_parent->_col == RED) {cout << "发现连续的红色节点" << endl;//不能有连续的红色节点return false;}if (root->_col == BLACK) blackcount++;return check(root->_left, reference, blackcount) && check(root->_right, reference, blackcount);}int _treeheight(Node* root) {if (root == nullptr)  return 0;int leftheight = _treeheight(root->_left);int rightheight = _treeheight(root->_right);return leftheight > rightheight ? leftheight + 1 : rightheight + 1;}int _size(Node* root) {if (root == nullptr) return 0;return _size(root->_left) + _size(root->_right) + 1;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}Node* _root=nullptr;};

测试用例:

#define _CRT_SECURE_NO_WARNINGS
#include"rbtree.h"int main() {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){t.insert({ e, e });}t.InOrder();cout << t.is_rbtree() << endl;cout <<"树的节点大小:"<< t.size() << endl;cout <<" 树的高度:"<< t.treeheight() << endl;}

四·红黑树创建总结(仅个人理解): 

首先的话,我们来谈一谈插入吧:这里可以这么理解:插入就按照二插搜索树插入,然后就是接下来要保证平衡和颜色了,我们插入的红色如果发现父亲是黑色就结束,如果红色,我们三步走:

第一步:判断父亲为爷爷的左还是右。

第二步:看叔叔存在为黑(或者不存在)还是为红。

第三步:看孩子连接父亲的左还是右。

下面画图帮助理解一下:

这里我们可能会说为什么只有叔叔是红色,才会孩子变爷爷向上调整,叔叔是其他就不用?因为当叔叔是其他的时候就会旋转,此时最顶上就是黑色,而旋转带来的改变犹如上调,故可以类似于做到了上调操作。 

本篇结束:希望对每一位读者通过此篇文章对有关红黑树的认识能有所增加,哪怕一丢丢,也更能体现这篇文章存在的价值,感谢各位的用心阅读以及支持!

相关文章:

RBTree(红黑树)的介绍和实现

欢迎来到杀马特的主页&#xff1a;羑悻的小杀马特.-CSDN博客 目录 ​编辑 一红黑树介绍&#xff1a; 1.1红黑树概念&#xff1a; 1.2红黑树遵循的原则&#xff1a; 1.3红黑树效率分析&#xff1a; 二.红黑树的实现&#xff1a; 2.1红黑树结构&#xff1a; 2.2红黑树节点…...

信息安全数学基础(30)指数及其基本性质

一、指数的定义 数学定义&#xff1a; 指数是幂运算a3表示3个a相乘。 经济学定义&#xff1a; 广义地讲&#xff0c;任何两个数值对比形成的相对数都可以称为指数。狭义地讲&#xff0c;指数是用于测定多个项目在不同场合下综合变动的一种特殊相对数。 二、指数的基本性质…...

Android开发首页底部tab切换图标有动画效果

Android开发首页底部tab切换图标有动画效果 主页tab切换很正常&#xff0c;但往往加上写动画更好看 一、思路&#xff1a; 用属性动画&#xff0c;并且事先准备多张图片&#xff0c;用于切换后播放动画 二、效果图&#xff1a; 单纯图看不出来&#xff0c;看下视频效果 An…...

Web前端高级工程师培训:异步处理专题

异步处理专题 课前准备 工具 编辑器 VSCode浏览器 Chorme 前置知识 ES6基础语法 课堂主题 同步及异步概念方块运动的实现promise的用法then的返还值Async 函数 和 await 课堂目标 理解并学会使用promise使用方式以及async 、await的使用 同步异步概念 js是单线程 单线程…...

ESP32-C3实现热点并在浏览器中输入域名访问网页

源代码 #include <WiFi.h> // 引入 WiFi 库&#xff0c;用于处理网络连接 #include <WebServer.h> // 引入 WebServer 库&#xff0c;用于创建 HTTP 服务器 #include <DNSServer.h> // 引入 DNSServer 库&#xff0c;用于处理 DNS 请…...

基于Python的自然语言处理系列(32):spaCy属性扩展

1. 介绍 在 spaCy 中,自定义扩展属性让我们能够为 Doc、Token 和 Span 对象添加元数据。通过这些扩展属性,开发者可以根据需要存储额外的上下文信息,或者动态计算属性值。 自定义属性通过点下划线(dot-underscore)属性访问,例如 token._.is_color。这种命名方式确保这些属…...

STM32 输入捕获模式详解:PWM 输入捕获与 PWI 模式(续篇)

在前两篇文章中&#xff0c;我们探讨了 STM32 输入捕获的基础和 PWI 模式的工作原理&#xff0c;特别是定时器的两个通道如何协同工作以捕获 PWM 信号。本文将进一步结合 STM32 标准库函数中的 TIM_PWMIConfig()&#xff0c;来讲解如何通过库函数配置定时器实现 PWI 模式。 我…...

【C++】set/map(重点解析)

目录 一、关联式容器和序列式容器 二、C中的键值对——pair 1.概念 2.定义 3.构造pair 三.set 1.construct构造 2.iterator迭代器 3.insert插入 4.erase删除 5.find查找 6.lower_bound和upper_bound 7.count 四.multiset 五.map 1.insert 2.operator[] 一、…...

【算法篇】动态规划类(1)(笔记)

目录 一、理论基础 1. 大纲 2. 动态规划的解题步骤 二、LeetCode 题目 1. 斐波那契数 2. 爬楼梯 3. 使用最小花费爬楼梯 4. 不同路径 5. 不同路径 II 6. 整数拆分 7. 不同的二叉搜索树 一、理论基础 1. 大纲 动态规划&#xff0c;英文&#xff1a;Dynamic Programm…...

mysql学习教程,从入门到精通,SQL 约束(Constraints)(41)

在数据库设计中&#xff0c;约束&#xff08;Constraints&#xff09;用于确保数据的准确性和完整性。它们通过限制可以插入到数据库表中的数据类型来防止无效数据。SQL 中有几种常见的约束类型&#xff0c;包括主键约束&#xff08;Primary Key&#xff09;、外键约束&#xf…...

使用CSS3与JavaScript实现炫酷的3D旋转魔方及九宫格交换动效

文章目录 前言一、项目需求背景二、CSS3 3D基础知识介绍2.1 什么是CSS3 3D&#xff1f;2.2 主要使用的CSS属性 三、使用HTML和CSS搭建魔方结构四、让魔方动起来&#xff1a;CSS3动画五、九宫格数字交换的JavaScript实现5.1 九宫格布局5.2 随机交换数字 六、随机交换与相邻格子的…...

springboot项目通过maven的profile功能实现通过不同文件夹的方式来组织不同环境配置文件

写在前面 本文看下springboot项目如何通过文件夹的方式来组织不同环境配置文件。 1&#xff1a;正文 一般的我们写springboot项目时配置文件是这个样子的&#xff1a; appliction.yaml --> 通过spring.profiles.activexxx来激活某个指定后缀的配置文件 application-evn1…...

GAN(Generative Adversarial Nets)

GAN(Generative Adversarial Nets) 引言 GAN由Ian J. Goodfellow等人提出&#xff0c;是Ian J. Goodfellow的代表作之一&#xff0c;他还出版了大家耳熟能详的花书&#xff08;Deep Learning深度学习&#xff09;&#xff0c;GAN主要的思想是同时训练两个模型&#xff0c;生成…...

linux下使用mpi求自然数和

搭建MPI并行计算环境&#xff0c;编写 MPI程序&#xff0c;求和 1 23....1 0000。 要求: 1.使用100个进程; 2.进程0计算1 2...100, 进程1计算101 102... 200, ..... 进程99计算9901 9902... 10000; 3.调用计时函数,分别输出每个进程的计算时间; 4.需使用MPI集群通信函数和同…...

WebGl学习使用attribute变量绘制一个水平移动的点

在WebGL编程中&#xff0c;attribute变量是一种特殊类型的变量&#xff0c;用于从客户端传递数据到顶点着色器。这些数据通常包括顶点的位置、颜色、纹理坐标等&#xff0c;它们是与每个顶点直接相关的信息。attribute变量在顶点着色器中声明&#xff0c;并且对于每个顶点来说都…...

机器学习四大框架详解及实战应用:PyTorch、TensorFlow、Keras、Scikit-learn

目录 框架概述PyTorch&#xff1a;灵活性与研究首选TensorFlow&#xff1a;谷歌加持的强大生态系统Keras&#xff1a;简洁明了的高层 APIScikit-learn&#xff1a;传统机器学习的必备工具实战案例 图像分类实战自然语言处理实战回归问题实战 各框架的对比总结选择合适的框架 1…...

linux源码安装slurm以及mung和openssl

一、源码安装munge 1、编译安装munge &#xff08;1&#xff09;下载munge地址&#xff1a;https://github.com/dun/munge/releases &#xff08;2&#xff09;解压编译安装&#xff1a; 1 2 3 4 5 6 7 8 创建/data目录 复制文件munge-0.5.15.tar.xz 到/data目录下 tar -Jx…...

分享蓝牙耳机A2DP音频卡顿原因及解决思路

背景 最近一直在更新博客&#xff0c;我觉得写博客有三个好处&#xff0c;一是很多东西时间久了就会忘&#xff0c;记下来方便自己以后回忆和总结&#xff0c;二是记下来可以加深自己对知识的理解&#xff0c;三是可以知识分享&#xff0c;方便他人。 言归正传&#xff0c;今天…...

Mac 下编译 libaom 源码教程

AV1 AV1是一种开放、免版税的视频编码格式&#xff0c;由开放媒体联盟&#xff08;AOMedia&#xff09;开发&#xff0c;旨在提供高压缩效率和优秀的视频质量。AV1支持多种分辨率&#xff0c;包括SD、HD、4K和8K&#xff0c;并适用于视频点播&#xff08;VOD&#xff09;、直播…...

【成品设计】基于Arduino平台的物联网智能灯

《基于Arduino平台的物联网智能灯》 整体功能&#xff1a; 这个任务中要求实现一个物联网智能灯。实际测试环境中要求设备能够自己创建一个热点&#xff0c;连接这个热点后能自动弹出控制界面&#xff08;强制门户&#xff09;。 功能点 基础功能 (60分) 要求作品至少有2个灯…...

安装和配置k8s可视化UI界面dashboard-1.20.6

安装和配置k8s可视化UI界面dashboard-1.20.6 1.环境规划2.初始化服务器1&#xff09;配置主机名2&#xff09;设置IP为静态IP3&#xff09;关闭selinux4&#xff09;配置主机hosts文件5&#xff09;配置服务器之间免密登录6&#xff09;关闭交换分区swap&#xff0c;提升性能7&…...

VLAN:虚拟局域网

VLAN:虚拟局域网 交换机和路由器协同工作后&#xff0c;将原先的一个广播域&#xff0c;逻辑上&#xff0c;切分为多个广播域。 第一步:创建VLAN [SW1]dispaly vlan 查询vlan VID&#xff08;VLAN ID&#xff09;:用来区分和标定不同的vlan 由12位二进制构成 范围: 0-4…...

利用可解释性技术增强制造质量预测模型

概述 论文地址&#xff1a;https://arxiv.org/abs/2403.18731 本研究提出了一种利用可解释性技术提高机器学习&#xff08;ML&#xff09;模型性能的方法。该方法已用于铣削质量预测&#xff0c;这一过程首先训练 ML 模型&#xff0c;然后使用可解释性技术识别不需要的特征并去…...

FlexMatch: Boosting Semi-Supervised Learning with Curriculum Pseudo Labeling

FlexMatch: Boosting Semi-Supervised Learning with Curriculum Pseudo Labeling 摘要:引言:背景3 flexMatch3.1 Curriculum Pseudo Labeling3.2 阈值预热3.3非线性映射函数实验4.1 主要结果4.2 ImageNet上的结果4.3收敛速度加速4.4 消融研究5 相关工作摘要: 最近提出的Fi…...

Spring Cloud 3.x 集成eureka快速入门Demo

1.什么是eureka&#xff1f; Eureka 由 Netflix 开发&#xff0c;是一种基于REST&#xff08;Representational State Transfer&#xff09;的服务&#xff0c;用于定位服务&#xff08;服务注册与发现&#xff09;&#xff0c;以实现中间层服务的负载均衡和故障转移&#xff…...

线性代数 矩阵

一、矩阵基础 1、定义 一组数按照矩形排列而成的数表&#xff1b;形似行列式&#xff0c;区别点是 矩阵行列式符号()或[]| |形状方阵或非方阵方阵本质数表数属性A|A|是A诸多属性中的一种维度m *n (m 与n可以相等也可以不相等)n*n 同型矩阵 若A、B两个矩阵都是mn 矩阵&#x…...

【C语言】使用结构体实现位段

文章目录 一、什么是位段二、位段的内存分配1.位段内存分配规则练习1练习2 三、位段的跨平台问题四、位段的应用五、位段使用的注意事项 一、什么是位段 在上一节中我们讲解了结构体&#xff0c;而位段的声明和结构是类似的&#xff0c;它们有两个不同之处&#xff0c;如下&…...

univer实现excel协同

快速入门 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><script src&q…...

JavaScript进阶笔记--深入对象-内置构造函数及案例

深入对象 创建对象三种方式 利用对象字面量new Object&#xff08;{…}&#xff09;利用构造函数 // 1. 字面量创建对象const obj1 {name: pig,age: 18};console.log(obj1); // {name: "pig", age: 18}// 2. 构造函数创建对象function Pig(name, age) {this.name…...

网络爬虫自动化Selenium模拟用户操作

自动化测试和网络爬虫在现代软件开发中占据着重要的位置。它们通过自动化用户操作,减少了人工重复操作的时间成本。Selenium作为一个功能强大且应用广泛的自动化工具,不仅能在不同的浏览器中运行自动化测试,还能进行跨平台测试,并允许与多种编程语言集成。本教程将介绍如何…...