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

C++数据结构:手撕红黑树

目录

一. 红黑树的概念及结构

二. 红黑树节点的定义

三. 红黑树节点的插入

3.1 初步查找插入节点的位置并插入节点

3.2 红黑树结构的调整

3.3 红黑树节点插入完整版代码

四. 红黑树的结构检查

4.1 检查是否为搜索树

4.2 检查节点颜色是否满足要求

附录:红黑树完整版代码


一. 红黑树的概念及结构

在我之前的博客C++数据结构:手撕AVL树_【Shine】光芒的博客-CSDN博客中对AVL树的结构及插入节点操作进行了分析,AVL树要求以每个节点为根节点的子树的左右子树高度差不超过1,以此来保证搜索树查找数据的时间复杂度为O(logN)。

但是,AVL树对高度差的要求过于严格,会导致在插入节点的过程中频繁进行旋转,造成数据插入效率低下。为了权衡插入数据与查找数据的效率,一种新的数据结构 -- 红黑树 被提出。相比于AVL树,红黑树对高度差的要求适当进行了放松,红黑树要求:最长路径的节点数目不超过最短路径的两倍。

红黑树的每个节点为红色或黑色,通过一定的规则控制节点颜色,来达到最长路径节点数目不超过最短路径节点数目两倍的要求,这也是红黑树名称的由来。

图1.1 红黑树的结构图

一颗结构正确的红黑树,要么为空树,要么满足一下几个条件:

  1. 每个节点为红色或者黑色。
  2. 根节点为黑色。
  3. 如果一个节点为红色,那么它的两个根节点一定为黑色,即:红黑树中没有连续的红色节点,但是,可以有连续的黑色节点。
  4. 对于每个节点,从该节点到其后代叶子结点的路径上,黑色节点的数目相同,即:每条路径的黑色节点数目相同。
  5. 叶子节点都为黑色节点。(注意:这里的叶子节点是指NULL节点)

为什么满足上面几条规则就能保证最长路径不超过最短路径两倍?

  • 极限最短:一条路径上全为黑色。
  • 极限最长:一黑一红间隔排布。

规则4要求每条路径上黑色节点数目相同,那么极限最短路径和极限最长路径肯定具有相同数目的黑色节点,假设每条路径上黑色节点数目为N,那么极限最短路径有N个节点,极限最长路径有2N个节点,这样就满足了红黑树的路径长度的要求。

二. 红黑树节点的定义

红黑树的节点应当被定义为一个三叉链,具有三个红黑树节点指针,分别为:指向左孩子节点的指针_left、指向右孩子节点的指针_right,指向父亲节点的指针_parent。这里存储父亲节点指针的目的是为了在插入数据后检查红黑树结构是否正确以及进行变色及旋转操作。

同时,还应当定义Color枚举常量,使用_col来表示节点颜色,并存储一键值对kv来表示节点数据。

代码2.1:(红黑树节点)

//枚举常量 -- 红色、黑色
enum Color
{RED,BLACK
};//定义红黑树节点
template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;std::pair<K, V> _kv;   //每个节点存储的键值对Color _col;   //节点颜色RBTreeNode(const std::pair<K, V>& kv)   //节点构造函数: _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){ }
};

三. 红黑树节点的插入

3.1 初步查找插入节点的位置并插入节点

红黑树节点插入位置的查找与普通搜索二叉树和AVL树均一致,流程为:

  • 如果当前位置为nullptr,那么该位置为插入节点的位置。
  • 如果当前节点值大于要插入的值,到该节点的左子树去查找。
  • 如果当前节点值小于要插入的值,到该节点的右子树去查找。
  • 如果当前节点值等于要插入的值,则插入失败。(二叉搜索树一般不允许存在相同的节点)。

当查找到插入位置后,判断是插入到了其父亲节点的左孩子位置还是右孩子位置,将新节点与其父亲节点进行连接。

注意:新插入的节点应初步设置为红色。因为:红黑树要求每条路径上黑色节点数目相同,而如果给定新插入的节点为黑色,那么根节点的另一颗没有插入数据的子树中也要想办法增加黑色节点数目或旋转来满足结构要求,这样后期调整红黑树结构就会变得复杂。而初步设定节点为红色,只是有可能存在连续红色节点,只需调整新节点所在子树节点的颜色或进行简单旋转即可。

3.2 红黑树结构的调整

红黑树结构调整主要涉及到四个节点,通过观察下面四个节点的颜色,进行分类讨论,采取不同的方法调整红黑树结构:

  1. cur节点 -- 新插入的节点。
  2. p节点 -- 父亲节点,p = cur->_parent。
  3. g节点 -- 祖父节点,g = p->_parent。
  4. u节点 -- 叔叔节点,与p具有共同父亲节点的节点。u->_parent = p->_parent = g。
图3.1 红黑树结构调整所涉及到的节点示意图

如果p节点为黑色节点,则红黑树已经满足结构要求,不需要再进行调整,如果p节点为红色,那么则会存在连续的红色节点,要进行调整。对于如何调整,分为两大种情况及数种细分情况进行讨论:

  1. cur为红,p为红,g为黑,u存在且为红。
  2. cur为红,p为红,g为黑,u不存在或u存在且为黑。

由此可以看出,u的颜色是如何调整的关键所在。

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

将p节点和u节点变为黑色,将g节点变为红色,然后将cur节点更新为g节点,将p节点更新为更新为g->_parent节点,继续向上调整。

图3.2  cur为红,p为红,g为黑,u存在且为红时红黑树调整示意图

 情况二:cur为红,p为红,g为黑,u不存在或u存在且为红。

  • 情况2.1:节点cur为p的左子节点,p为g的左子节点  -- 右单旋 + 变色

先对g节点进行右单旋操作,然后将p节点变为黑色,g节点变为红色。

图3.3  右单旋 + 变色 示意图
  •  情况2.2:节点cur为p的右子节点,p为g的右子节点 -- 左单旋 + 变色

先对g节点进行左单旋操作,然后将p节点变为黑色,g节点变为红色。

图3.4 左单旋 + 变色 示意图 
  •  情况2.3:节点cur为p的右子节点,p为g的左子节点 -- 左右双旋 + 变色

先对p节点进行左单旋,然后对g节点进行右单旋,最后将cur节点变为黑色,将g节点变为红色。

图3.5  左右双旋 + 变色 示意图
  •  情况2.4:节点cur为p的左子节点,节点p为g的右子节点 -- 右左双旋 + 变色

先对p节点进行右单旋,然后对g节点进行左单旋,最后将cur节点变为黑色,将g节点变为红色。 

图3.6  右左双旋 + 变色 示意图

3.3 红黑树节点插入完整版代码

	bool insert(const std::pair<K, V>& kv){//插入第一个节点if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;   //根节点为黑色return true;}//寻找节点插入的位置Node* parent = nullptr;   Node* cur = _root;while (cur){//如果cur节点的key值大于插入键值对的key,向左子树查找if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if(cur->_kv.first < kv.first)  //如果cur节点的key值小于插入键值对的key,向左子树查找{parent = cur;cur = cur->_right;}else  //相等表明节点已存在,插入失败{return false;}}//判断新节点是parent的左节点还是右节点,链接//默认新插入的节点为红色cur = new Node(kv);cur->_col = RED;cur->_parent = parent;if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}//如果parent节点不为空且为红色,那么红黑树的结构在插入节点后被破坏,需要调整while (parent && parent->_col == RED){Node* grandParent = parent->_parent;   //祖父节点assert(grandParent);assert(grandParent->_col == BLACK);   //断言检查,如果祖父节点为空或为黑色,那么红黑树结构在节点插入之前就存在问题if (parent == grandParent->_left)  //插入在祖父节点的左子树{Node* uncle = grandParent->_right;//情况一:cur为红,parent为红,grandFather为黑,uncle为红if (uncle && uncle->_col == RED){//将parent节点和uncle节点变为黑,grandFather节点变为红,然后继续向上调整parent->_col = BLACK;uncle->_col = BLACK;grandParent->_col = RED;cur = grandParent;parent = cur->_parent;}	else  //情况二、三:cur为红,parent为红,grandFather为黑,uncle不存在或为黑{if (parent->_left == cur){//情况二 -- 进行右单旋 + 变色(parent变黑,grandFather变红)//    g//  p   u//cRotateR(grandParent);parent->_col = BLACK;grandParent->_col = RED;}else{//情况三 -- 进行左右双旋 + 变色(cur节点变为黑,grandFater节点变为红)//    g//  p   u//   u RotateLR(grandParent);cur->_col = BLACK;grandParent->_col = RED;}break;}}else  //parent == grandParent->_right{Node* uncle = grandParent->_left;  //叔叔节点//情况一:cur为红,parent为红,grandFather为黑,uncle为红if (uncle && uncle->_col == RED){//将parent节点和uncle节点变为黑,grandFather节点变为红,然后继续向上调整parent->_col = BLACK;uncle->_col = BLACK;grandParent->_col = RED;cur = grandParent;parent = cur->_parent;}else{//情况二、三:cur为红,parent为红,grandFather为黑,uncle不存在或为黑if (parent->_right == cur){//情况二 -- 进行右单旋 + 变色(parent变黑,grandFather变红)//   g// u   p//       cRotateL(grandParent);parent->_col = BLACK;grandParent->_col = RED;}else{//情况三 -- 进行右左双旋 + 变色(cur节点变为黑,grandFater节点变为红)//    g// u     p//     cRotateRL(grandParent);cur->_col = BLACK;grandParent->_col = RED;}break;}}}_root->_col = BLACK;   //根节点为黑色return true;}void RotateR(Node* parent)   //右单旋函数{Node* pNode = parent->_parent;    Node* pL = parent->_left;   //左子节点Node* pLR = pL->_right;   //左子节点的右子节点//将pLR节点托管给parent节点的左子节点parent->_left = pLR;if (pLR != nullptr){pLR->_parent = parent;}//将父亲节点托管给pL节点的右子节点pL->_right = parent;  parent->_parent = pL;//此时这颗进行旋转的子树的根节点变为了pL,pL要与pNode节点连接if (parent == _root){_root = pL;pL->_parent = nullptr;}else{pL->_parent = pNode;if (pNode->_left == parent){pNode->_left = pL;}else{pNode->_right = pL;}}}void RotateL(Node* parent)   //左单旋函数{Node* pNode = parent->_parent;Node* pR = parent->_right;    //右子节点Node* pRL = pR->_left;   //右子节点的左子节点//将pLR节点托管给parent节点的右子节点parent->_right = pRL;if (pRL != nullptr){pRL->_parent = parent;}//将parent节点托管给pR的左子节点pR->_left = parent;parent->_parent = pR;if (_root == parent){_root = pR;_root->_parent = nullptr;}else{pR->_parent = pNode;if (pNode->_left == parent){pNode->_left = pR;}else{pNode->_right = pR;}}}void RotateLR(Node* parent)  //左右双旋函数{RotateL(parent->_left);RotateR(parent);}void RotateRL(Node* parent)  //右左双旋函数{RotateR(parent->_right);RotateL(parent);}

四. 红黑树的结构检查

4.1 检查是否为搜索树

采用中序遍历,得到一串递增的数据即可证明为搜索树。

代码4.1:(中序遍历)

	//中序遍历函数void InOrder(){_InOrder(_root);std::cout << std::endl;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);std::cout << root->_kv.first << " ";_InOrder(root->_right);}

4.2 检查节点颜色是否满足要求

需要进行以下两个方面的检查:

  • 检查每条路径上的黑色节点数量是否相同。
  • 检查是否不存在连续的红色节点。

检查每条路径上的黑色节点数目时,可以选取其中一条路径作为基准(一般为最左侧路径或最右侧路径),通过函数遍历每条路径,获取每条路径上的黑色节点数目,与基准路径的黑色节点数目进行比较,如果不相同,则不满足红黑树结构要求。

图4.1 基准路径的选择

红黑树要求红色节点的左右孩子节点必须为黑色,但是对孩子节点颜色进行检查较为繁琐,因此,当遇到红色节点时,检查其父亲节点是否为空或者为黑色即可,如果红色节点的父亲节点依旧为红色,则表明树中存在连续的红色节点,不满足红黑树结构要求。

代码4.2:(节点颜色检查)

	//红黑树检验函数bool IsRBTree(){//空树是合法的红黑树if (_root == nullptr){return true;}//检查根节点颜色if (_root->_col == RED){std::cout << "根节点颜色不是黑色" << std::endl;}int baseBlackNum = 0;   //基准黑色节点个数//以最左侧路径为基准,计算黑色节点个数,每条路径黑色节点数目都应该相同Node* cur = _root;while (cur){if (cur->_col == BLACK){++baseBlackNum;}cur = cur->_left;}bool blackNumTrue = PrevCheck(_root, 0, baseBlackNum);   //检查每条路径黑色节点数目是否相同bool colorTrue = CheckColor(_root);  //检查是否存在连续红色节点return blackNumTrue && colorTrue;}bool CheckColor(Node* root){if (root == nullptr){return true;}//如果本节点为红色且父亲节点也为红色,证明存在连续红色节点,结构错误if (root->_col == RED && root->_parent && root->_parent->_col == RED){std::cout << "存在连续的红色节点" << std::endl;return false;}return CheckColor(root->_left) && CheckColor(root->_right);}bool PrevCheck(Node* root, int blackNum, int baseBlackNum){if (root == nullptr){if (blackNum != baseBlackNum){std::cout << "每条路径上黑色节点的数目不同" << std::endl;return false;}else{return true;}}if (root->_col == BLACK){++blackNum;}return PrevCheck(root->_left, blackNum, baseBlackNum)&& PrevCheck(root->_right, blackNum, baseBlackNum);}

附录:红黑树完整版代码

//枚举常量 -- 红色、黑色
enum Color
{RED,BLACK
};//定义红黑树节点
template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;std::pair<K, V> _kv;   //每个节点存储的键值对Color _col;   //节点颜色RBTreeNode(const std::pair<K, V>& kv)   //节点构造函数: _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){ }
};//红黑树类模板
template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;    //类型重定义红黑树节点public:bool insert(const std::pair<K, V>& kv){//插入第一个节点if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;   //根节点为黑色return true;}//寻找节点插入的位置Node* parent = nullptr;   Node* cur = _root;while (cur){//如果cur节点的key值大于插入键值对的key,向左子树查找if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if(cur->_kv.first < kv.first)  //如果cur节点的key值小于插入键值对的key,向左子树查找{parent = cur;cur = cur->_right;}else  //相等表明节点已存在,插入失败{return false;}}//判断新节点是parent的左节点还是右节点,链接//默认新插入的节点为红色cur = new Node(kv);cur->_col = RED;cur->_parent = parent;if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}//如果parent节点不为空且为红色,那么红黑树的结构在插入节点后被破坏,需要调整while (parent && parent->_col == RED){Node* grandParent = parent->_parent;   //祖父节点assert(grandParent);assert(grandParent->_col == BLACK);   //断言检查,如果祖父节点为空或为黑色,那么红黑树结构在节点插入之前就存在问题if (parent == grandParent->_left)  //插入在祖父节点的左子树{Node* uncle = grandParent->_right;//情况一:cur为红,parent为红,grandFather为黑,uncle为红if (uncle && uncle->_col == RED){//将parent节点和uncle节点变为黑,grandFather节点变为红,然后继续向上调整parent->_col = BLACK;uncle->_col = BLACK;grandParent->_col = RED;cur = grandParent;parent = cur->_parent;}	else  //情况二、三:cur为红,parent为红,grandFather为黑,uncle不存在或为黑{if (parent->_left == cur){//情况二 -- 进行右单旋 + 变色(parent变黑,grandFather变红)//    g//  p   u//cRotateR(grandParent);parent->_col = BLACK;grandParent->_col = RED;}else{//情况三 -- 进行左右双旋 + 变色(cur节点变为黑,grandFater节点变为红)//    g//  p   u//   u RotateLR(grandParent);cur->_col = BLACK;grandParent->_col = RED;}break;}}else  //parent == grandParent->_right{Node* uncle = grandParent->_left;  //叔叔节点//情况一:cur为红,parent为红,grandFather为黑,uncle为红if (uncle && uncle->_col == RED){//将parent节点和uncle节点变为黑,grandFather节点变为红,然后继续向上调整parent->_col = BLACK;uncle->_col = BLACK;grandParent->_col = RED;cur = grandParent;parent = cur->_parent;}else{//情况二、三:cur为红,parent为红,grandFather为黑,uncle不存在或为黑if (parent->_right == cur){//情况二 -- 进行右单旋 + 变色(parent变黑,grandFather变红)//   g// u   p//       cRotateL(grandParent);parent->_col = BLACK;grandParent->_col = RED;}else{//情况三 -- 进行右左双旋 + 变色(cur节点变为黑,grandFater节点变为红)//    g// u     p//     cRotateRL(grandParent);cur->_col = BLACK;grandParent->_col = RED;}break;}}}_root->_col = BLACK;   //根节点为黑色return true;}//中序遍历函数void InOrder(){_InOrder(_root);std::cout << std::endl;}//红黑树检验函数bool IsRBTree(){//空树是合法的红黑树if (_root == nullptr){return true;}//检查根节点颜色if (_root->_col == RED){std::cout << "根节点颜色不是黑色" << std::endl;}int baseBlackNum = 0;   //基准黑色节点个数//以最左侧路径为基准,计算黑色节点个数,每条路径黑色节点数目都应该相同Node* cur = _root;while (cur){if (cur->_col == BLACK){++baseBlackNum;}cur = cur->_left;}bool blackNumTrue = PrevCheck(_root, 0, baseBlackNum);   //检查每条路径黑色节点数目是否相同bool colorTrue = CheckColor(_root);  //检查是否存在连续红色节点return blackNumTrue && colorTrue;}private:bool CheckColor(Node* root){if (root == nullptr){return true;}//如果本节点为红色且父亲节点也为红色,证明存在连续红色节点,结构错误if (root->_col == RED && root->_parent && root->_parent->_col == RED){std::cout << "存在连续的红色节点" << std::endl;return false;}return CheckColor(root->_left) && CheckColor(root->_right);}bool PrevCheck(Node* root, int blackNum, int baseBlackNum){if (root == nullptr){if (blackNum != baseBlackNum){std::cout << "每条路径上黑色节点的数目不同" << std::endl;return false;}else{return true;}}if (root->_col == BLACK){++blackNum;}return PrevCheck(root->_left, blackNum, baseBlackNum)&& PrevCheck(root->_right, blackNum, baseBlackNum);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);std::cout << root->_kv.first << " ";_InOrder(root->_right);}void RotateR(Node* parent)   //右单旋函数{Node* pNode = parent->_parent;    Node* pL = parent->_left;   //左子节点Node* pLR = pL->_right;   //左子节点的右子节点//将pLR节点托管给parent节点的左子节点parent->_left = pLR;if (pLR != nullptr){pLR->_parent = parent;}//将父亲节点托管给pL节点的右子节点pL->_right = parent;  parent->_parent = pL;//此时这颗进行旋转的子树的根节点变为了pL,pL要与pNode节点连接if (parent == _root){_root = pL;pL->_parent = nullptr;}else{pL->_parent = pNode;if (pNode->_left == parent){pNode->_left = pL;}else{pNode->_right = pL;}}}void RotateL(Node* parent)   //左单旋函数{Node* pNode = parent->_parent;Node* pR = parent->_right;    //右子节点Node* pRL = pR->_left;   //右子节点的左子节点//将pLR节点托管给parent节点的右子节点parent->_right = pRL;if (pRL != nullptr){pRL->_parent = parent;}//将parent节点托管给pR的左子节点pR->_left = parent;parent->_parent = pR;if (_root == parent){_root = pR;_root->_parent = nullptr;}else{pR->_parent = pNode;if (pNode->_left == parent){pNode->_left = pR;}else{pNode->_right = pR;}}}void RotateLR(Node* parent)  //左右双旋函数{RotateL(parent->_left);RotateR(parent);}void RotateRL(Node* parent)  //右左双旋函数{RotateR(parent->_right);RotateL(parent);}private:Node* _root = nullptr;
};

相关文章:

C++数据结构:手撕红黑树

目录 一. 红黑树的概念及结构 二. 红黑树节点的定义 三. 红黑树节点的插入 3.1 初步查找插入节点的位置并插入节点 3.2 红黑树结构的调整 3.3 红黑树节点插入完整版代码 四. 红黑树的结构检查 4.1 检查是否为搜索树 4.2 检查节点颜色是否满足要求 附录&#xff1a;红黑…...

Spring IoC 深度学习

Io回顾 IoC 是 Inversion of Control 的简写&#xff0c;译为“控制反转”&#xff0c;它不是一门技术&#xff0c;而是一种设计思想&#xff0c;是一个重要的面向对象编程法则&#xff0c;能够指导我们如何设计出松耦合、更优良的程序。 Spring 通过 IoC 容器来管理所有 Jav…...

C语言从入门到精通第17天(指针和数组联用)

指针和数组联用 不同类型指针变量之间的区别数组的指针指针数组 不同类型指针变量之间的区别 在了解数组和指针联用之前&#xff0c;我们先对指针变量进行补充。我们对比一下int *p1和char *p2的区别&#xff1f; 相同点&#xff1a; 都是指针变量都是用来保存一个内存地址编…...

Android9.0 原生系统SystemUI下拉状态栏和通知栏视图之锁屏通知布局

1.前言 在9.0的系统rom定制化开发中,对于系统原生systemui的锁屏界面的功能也是非常重要的,所以在锁屏页面布局中,也是有通知栏布局的,所以接下来对于息屏亮屏 通知栏布局的相关流程分析,看下亮屏后锁屏页面做了哪些功能 2.原生系统SystemUI下拉状态栏和通知栏视图之锁…...

音视频八股文(10)-- mp4结构

介绍 mp4⽂件格式⼜被称为MPEG-4 Part 14&#xff0c;出⾃MPEG-4标准第14部分 。它是⼀种多媒体格式容器&#xff0c;⼴泛⽤于包装视频和⾳频数据流、海报、字幕和元数据等。&#xff08;顺便⼀提&#xff0c;⽬前流⾏的视频编码格式AVC/H264 定义在MPEG-4 Part 10&#xff09…...

python算法中的深度学习算法之深度信念网络(详解)

目录 学习目标: 学习内容: 深度信念网络 Ⅰ. 预训练 Ⅱ. 微调 学习目标: 一分钟掌握 python算法中的深度学习算法之深度信念网络 入门知识...

SPSS如何绘制常用统计图之案例实训?

文章目录 0.引言1.绘制简单条形图2.绘制分类条形图3.绘制分段条形图4.绘制简单线图5.绘制多重线图6.绘制垂直线图7.绘制简单面积图8.绘制堆积面积图9.绘制饼图10.绘制直方图11.绘制简单散点图12.绘制重叠散点图13.绘制矩阵散点图14.绘制三维散点图15.绘制简单箱图16.绘制分类箱…...

打动人心的故事 | 如何利用文案在Facebook上塑造品牌形象

在当今的数字营销时代&#xff0c;文案已经成为各大平台上不可或缺的元素之一。在Facebook上&#xff0c;一个好的文案能够为品牌带来巨大的曝光率和用户黏性&#xff0c;甚至可以改变用户对品牌的看法。那么&#xff0c;如何利用文案在Facebook上打动人心&#xff0c;塑造品牌…...

什么是模糊控制?

模糊控制设计原理 1、传统控制系统和模糊控制系统 传统控制系统结构&#xff1a; 控制目的&#xff1a;通过控制器调节控制信号u&#xff0c;使输出信号y达到要求 模糊控制系统结构&#xff1a; 与传统控制系统的差异&#xff1a;用模糊控制器FC&#xff08;Fuzzy Controller&…...

仿抖音开发需要注意的问题

一、版权问题 仿抖音开发需要注意版权问题&#xff0c;包括内容的版权和软件的版权。在开发的过程中&#xff0c;不要直接抄袭他人的作品&#xff0c;应该注重保护知识产权。 二、安全性问题 仿抖音开发需要重视应用的安全性问题&#xff0c;避免应用在使用过程中发生安全漏…...

如何根据期刊缩写查找期刊?

英文论文写作中&#xff0c;经常会插入参考文献。参考文献中的期刊名称&#xff0c;时常需要使用缩写。或者是手头有期刊缩写后的名称&#xff0c;但是有时候&#xff0c;查了半天也查不到期刊期刊全称&#xff0c;费时费力让人崩溃。今天就给各位学者老师总结一些查询期刊缩写…...

数据发送流程

在发送模式下&#xff0c;UART 的串行数据发送电路主要包括一个发送移位寄存器(TSR)&#xff0c;TSR 功能是将数据 逐个移位送出。待发数据必须先写到发送缓冲区中。 TXIFx 是发送中断标志位&#xff0c;可配置为发送缓冲区空或TSR 空。 数据的发送支持7bit 、8bit 或9bit 数据…...

堆及其应用

堆是一种基于树结构的数据结构&#xff0c;通常用于实现优先队列。堆分为最大堆和最小堆两种类型&#xff0c;最大堆的每个节点的值都大于等于其子节点的值&#xff0c;最小堆则相反&#xff0c;每个节点的值都小于等于其子节点的值。 基础算法操作包括&#xff1a; 1. 插入元…...

MySQL数据库备份脚本

PS&#xff1a;此脚本简单易懂&#xff0c;根据实际情况修改个别参数测试后即可使用&#xff0c;如有错误请指出&#xff01; 1.MySQL数据库备份脚本 #!/bin/bashuser pw ip dateYdate "%Y" date2date "%Y%m%d" date3date "%Y%m%d %H:%M" date…...

【2023 · CANN训练营第一季】应用开发深入讲解——第三章应用调试

学习资源 日志参考文档 应用开发FAQ 日志主要用于记录系统的运行过程及异常信息&#xff0c;帮助快速定位系统运行过程中出现的问题以及开发过程中的程序调试问题。 日志分为如下两大类&#xff1a; 系统类日志&#xff1a;系统运行产生的日志。主要包括&#xff1a; Contro…...

黎曼几何与黎曼流形

目录 0.黎曼几何 1. 欧几里得几何与黎曼几何的区别 2.黎曼流形 3.黎曼距离 4.切空间 5.黎曼均值 6. SPD矩阵如何形成黎曼流型 7.切线空间映射 8.同余变换和同余不变 9.黎曼对齐 科普性笔记&#xff0c;做了解&#xff0c;不深入。 0.黎曼几何 黎曼几何是一种基于欧几…...

lua | 运算符与字符串

目录 一、运算符 算数运算符 关系运算符 逻辑运算符 其他运算符 运算符优先级 二、字符串 转义字符 方法与用途 字符串截取 字符串大小转换 字符串查找与反转 字符串格式化 字符与整数的转换 匹配模式 本文章为笔者学习分享 学习网站&#xff1a;Lua 基本语法 | …...

NetBackup 10.2 新功能介绍:PostgreSQL 和 MySQL 自动化恢复达成

NetBackup 10.2 新功能介绍&#xff1a;PostgreSQL 和 MySQL 自动化恢复达成 原文来自&#xff1a;VERITAS 中文社区 2023-04-27 在执行恢复任务时&#xff0c;手动提取、更新数据库和实例并将其附加到 PostgreSQL 和 MySQL 是常规操作。而在最新的 NetBackup 10.2 版本中&am…...

ADRV9002官方例程开发过程中遇到的问题

开发环境&#xff1a;Vivado2021.2 HDL版本&#xff1a;hdl_2021_r2 GitHub - analogdevicesinc/hdl at hdl_2021_r2 no-OS版本&#xff1a;no_OS-2021_R2 GitHub - analogdevicesinc/no-OS at 2021_R2 &#xff08;PS&#xff1a;也可以用Vivado2019.1开发&#xff0c…...

Figma转换为sketch,分享这3款工具

在我们的设计工作中&#xff0c;我们经常会遇到各种各样的设计文件相互转换的问题。 你经常为此头疼吗&#xff1f;当你遇到Figma转换Sketch文件的问题时&#xff0c;你是如何解决的&#xff1f;Figma转换Sketch文件有工具吗&#xff1f; 根据众多设计师的经验&#xff0c;本…...

淘宝天猫1688京东商品详情API接口,封装接口可高并发

要提供商品详情数据需要知道具体的商品信息&#xff0c;但通常商品详情数据应包括以下内容&#xff1a; 商品名称&#xff1a;商品的名称&#xff0c;以方便顾客对其进行识别和区分。 商品描述&#xff1a;一段让顾客能够全面认识商品的描述。应能够有效地展示商品的特性、功能…...

虹科荣誉 | 虹科工业物联网产品荣获中国自动化产业年会用户信赖产品奖!

2023 虹科荣获2021年度中国自动化产业年会用户信赖产品奖 近日&#xff0c;2023中国自动化产业年会于北京隆重举行。虹科工业物联网的产品“OPC UA Tunneller软件”凭借其产品优势和市场美誉度&#xff0c;通过层层选拔&#xff0c;在本次大会中荣获2021年度用户信赖产品奖。…...

SwiftUI 如何让文本自动支持查找和替换功能?

概览 有些情况下&#xff0c;我们需要为文本编辑器实现文本的查找和替换功能&#xff08;find & replace&#xff09;&#xff0c;如果完全靠自已撸码还是比较棘手的。 所幸的是&#xff0c;从 SwiftUI 4.0 &#xff08;iOS 16&#xff09;开始&#xff0c;Apple 已经将查…...

SpringCloud全面学习笔记之初尝美妙篇

目录 前言初识微服务单体架构分布式架构微服务架构初见SpringCloud微服务治理分布式服务架构案例 微服务组件及使用Eureka注册中心提供者和消费者Eureka的结构和作用搭建Eureka服务注册服务服务发现Eureka注册服务总结 Ribbon负载均衡原理负载均衡原理负载均衡策略懒加载 Nacos…...

Spring MVC框架

Spring MVC框架 Spring MVC属于SpringFrameWork的后续产品&#xff0c;已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构&#xff0c;从而在使用Spring进行WEB开发时&#xff0c;可以选择使用Spring的Spri…...

Illustrator如何使用图层与蒙版之实例演示?

文章目录 0.引言1.绘制可爱冰淇淋图标2.霓虹渐变立体文字海报3.炫彩花纹背景 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对Illustrator进行了学习&#xff0c;本文通过《Illustrator CC2018基础与实战》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;…...

Office Tool Plus的使用

是否为安装&#xff0c;卸载&#xff0c;激活Office而烦恼&#xff1f; 下载 地址&#xff1a;Office Tool Plus 官方网站 - 一键部署 Office 安装office 先安装Office&#xff0c;Office_Pro_Plus_2021_LTSCProjectVisio_x64_zh_CN_VL_2022-02 注意&#xff0c;要安装批量…...

​射频PCB 设计​的六大条技巧

即使是最自信的设计人员&#xff0c;对于射频电路也往往望而却步&#xff0c;因为它会带来巨大的设计挑战&#xff0c;并且需要专业的设计和分析工具。这里将为您介绍六条技巧&#xff0c;来帮助您简化任何射频PCB 设计任务和减轻工作压力&#xff01; 1、保持完好、精确的射频…...

优化了成本和安装难度后,UWB信标能否取代蓝牙信标?

1 我们做安U3号是要解决什么问题&#xff1f; &#xff08;1&#xff09;信标式设计&#xff0c;解决传统UWB基站安装过程繁琐复杂的问题 传统UWB基站在安装过程中遇上的难题&#xff1a; l 安装位置选取问题&#xff1a;UWB基站的准确度与其安装位置有很大关系&#xff0c;…...

深入理解Java虚拟机——垃圾回收算法

1.前言 垃圾回收需要完成的三件事 首先我们需要明白垃圾回收需要完成的三件事&#xff1a; 哪些内存需要回收 堆内存中的对象所使用的内存方法区中的废弃的常量以及不再使用的类型 什么时候回收 当对象死亡方法区中某些内容&#xff08;常量和类型&#xff09;不再被使用 如…...