c++ 红黑树学习及简单实现

1. 了解红黑树

1.1. 概念

红黑树,是一种二叉搜索树,但在每个节点增加一个存储位表示节点的颜色,可以是红色,或是黑色,通过对任何一条从根到叶子的路径上各个节点的着色方式进行限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡。
都是搜索二叉树,红黑树和AVL树有什么区别?
AVL 树:左右高度差不超过1(严格平衡)
红黑树:最长路径不超过最短路径的两倍(近似平衡)
这样看起来,似乎 AVL树更优秀,严格平衡
如果相同的数据,AVL 树可能是 18层,但是 红黑树有可能是 18 - 36 层
在查找方面 AVL 树可能会比红黑树优秀一点,但是只是一点。
因为18 层的数据,2^17 ==131072,18层数据就有 这么多节点,查找 18 次和 查找 35次,对计算机来说区别很小。
同时还要注意,AVL树的严格平衡,因为严格平衡,所以 AVL树为了保证平衡,需要不停的旋转,因此在插入删除上,效率会慢很多
但是红黑树并不是严格平衡,所以在调整上,不会进行那么多次的调整,因此插入和删除的时间就会少很多。

1.2. 性质

先看下面一棵树
在这里插入图片描述
红黑树性质:

  1. 每个节点不是红色就是黑色。
  2. 根节点是黑色的。
  3. 如果一个节点是红色的,则它的两个节点都是黑色的(不能出现连续的红色节点,可以出现的组合:黑+黑, 黑+红, 红+黑)。
  4. 对于每个节点,从该节点到其所有后代中,均包含数目相同的黑色节点(每条路径都包含相同数量的黑色节点)。
  5. 每个叶子节点(NIL节点)都是黑色的。

为什么满足上面的性质,红黑树就能保证:最长路径中节点个数不会超过最短路径节点个数的两倍?
最短路径:全黑
最长路径:一黑一红间隔
在这里插入图片描述
假设最短上有 N 个节点
其他路径上的节点数量都在 [N, 2N] 之间
每条路径上黑色节点数量相同,最长的情况下是一红一黑间隔,同一条路径下,红节点数量和黑节点数量相同,也就是2
N
NIL 节点(叶子节点的空节点)
在这里插入图片描述
比如这棵树,这里看起来有 4 条理解,其实有 8 条
NIL 节点也要被算入路径内
路径是从跟节点走到空,才算路径。
在这里插入图片描述
这棵树也算红黑树,这棵树有 7 条路径。
在这里插入图片描述
这棵树,我们看起来好像是一棵红黑树,但是当我们把所有的 NIL 节点全部标出来
在这里插入图片描述

这样就能清楚的看到,标记位置的右子树只含有一个黑色节点,但是左子树的每条路径含有 两个黑色节点,所以 这棵树并不是红黑树

2. 模拟实现

2.1. 节点

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

这里的节点和 AVL 树类似,不过 红黑树 是用颜色来控制的,所以这里我们添加了 _col 来控制。
不够在这里的 构造函数,我们还不能确定 _col 初始化成什么样子,我们后面再看。

2.2. insert

首先 红黑树 还是搜索二叉树,所以插入的基本逻辑还是那套,我们先实现这部分,后面主要分析调整的地方。

template<class k, class V>
class RBTree
{
public:typedef RBTreeNode<K, V> Node;RBTree():_root(nullptr){}bool insert(const pair<K, V>* kv){if(_root == nullptr){_root = new Node;return true;}Node* cur = _root;Node* parent = nullptr;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(cur);if(parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}return true;}
private:Node* _root;
};

二叉树的插入,这段没什么好说的,接下来看调整。
如果是插入节点,最开始应该插入什么颜色的节点最好?
我们都分析一下
在这里插入图片描述
如果我们在上面这棵树上插入节点,插入黑色节点后,我们发现,这条路径上的黑色节点数量发生变化,所以这里必须调整。
如果插入的是红色节点,每条路径上的黑色节点数量并未发生变化,这里可以不需要调整。
所以我们最好把 插入的节点初始化为 红色节点
在这里插入图片描述
在这里插入图片描述
但这不代表插入红色节点完全不需要修改,插入红色节点后,主要影响的是父节点。
这里插入分为 3 类型的情况:

  1. 如果新增的节点是 黑色节点,会影响所有父亲节点下面的路径。
  2. 如果新增的节点是 红色节点,父亲节点是 黑色节点,不用影响。
  3. 如果新增的节点是 红色节点, 父亲节点是 红色节点, 需要进行调整。

我们需要处理的情况是第 3 种。
在这里插入图片描述
如果出现这种情况,我们首先考虑把父亲变色。
如果仅靠变色不能解决问题,就需要通过 旋转 + 变色。
在这里插入图片描述

第一步,7 变 黑,因为 7 的父节点一定是 黑色,所以还要处理 父节点(7原本是红色,红色节点的父亲只能是黑色,7 所在的路径上多了一个黑色节点,所以还是要从 7 的父亲节点入手处理)。
第二步,7 的 叔叔节点 5 变 黑,但是又会导致 6 的两条路径上分别多出来一个 黑色节点,所以我们还需要把 6 变 红,从而使每条路径黑色节点数量想同。
这里调整自己路径后,还要通过自己的根去调整另一条路径,我们这里默认的情况是 叔叔节点 存在 且 和 7 都是红色。
那么叔叔不存在的情况,或者叔叔颜色原本为黑呢?
叔叔 不存在时:
在这里插入图片描述
这两棵树,如果是前面学过 AVL 树的人,应该能一眼看出来,这里是 右左双旋 和 右右左单旋。
在这里插入图片描述
旋转 + 变色 处理。
当然,这是基于 叔叔节点不存在的情况,叔叔节点存在且为黑的情况还要讨论,现在我们慢慢分析怎么用代码实现这些操作

2.2.1. 调整

我们先把需要调整的情况主要分为三种
在这里插入图片描述先确定节点
插入的节点: cur
插入节点的父节点: parent - p
插入节点的父节点的父节点: grandfather - g
叔叔节点: uncle - u
a,b,c,d,e 子树(可能为空,也可能是一整颗树)

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

首先是 第一种 情况:=
cur 为红,p 为红,g 为黑, u存在且为红
解决方法:将 p ,u 改为黑色, g 改为红色, 然后把 g 当做 cur,继续向上处理
在这里插入图片描述
但是简简单单变个色就行了吗?
这里还是有特殊情况需要处理的

  1. 如果 g 是 根节点
  2. 如果 g 的父节点是 红色
    在这里插入图片描述

第一种情况很简单,直接把 _root 变黑色就行了。
毕竟不能出现连续的红色节点,但是可以出现连续的黑色节点,所以这样变完全没有问题
那第二种呢,修改后,g 变成了 红色,但是如果 g 是一棵树的子树,同时 g 的父亲节点是 红色呢?
我们的子树是没有问题了,所以我们把 原树的 g 当做新的 cur 向上调整。
在这里插入图片描述
如果 修改后的树 的父亲节点是黑色,那么此时直接退出即可(原本g为根节点的树每条路径是 一个 黑色节点,修改后每条路径仍是一个黑色节点,对 g 以上的根节点来说,每天路径上的黑色节点数量没有发生改变,所以不需要向上调整)
这里我们像 AVL 树那样分析分析,是不是 只需要改变这 4 个节点才能完成操作?
在这里插入图片描述
还是这棵树,此时 a,b,c,d,e 的情况有点复杂
如果 a/b/c/d/e 都是 空,cur 就是新增节点。
如果 a/b 不是空节点,但是想要触发这个调整,必须在 a,b任意位置插入节点就会破坏规则
至于 c,d,e 可能是下面的任意一种情况
在这里插入图片描述
c,d,e 是每条路径上含有一个黑色节点的红黑树。
a,b 作为之前新插入的节点。
在 a,b 下任意位置插入节点都会破坏规则。
所以这里可能存在的树 (4x4x4)x4 = 256种
不管再怎么复杂,对应的子树都是经过调整后的 红黑树,或者说下面调整玩完成后向上调整到这个位置,所以我们只需要关注这里的 cur 就行。

第二种情况:
cur 为红,p为红, g 为黑, u不存在
在这里插入图片描述
如果遇到单纯变色不能解决问题的情况,这里就需要先旋转后变色。
解决方法:左左右单旋,旋转后,g变红, p变黑
这里一般没有什么特殊情况,最后根节点也处理成黑色,不会和父节点冲突
第三种情况:
cur 为红,p为红, g 为黑, u为黑
在这里插入图片描述
这种情况会出现吗,这里 u 可能是上一次调整后的结构,g 可能是根节点,所以会出现这种情况。
这里的处理方法,还是先进行旋转。
但是还是要看 cur 的位置
p 为 g 的左孩子,cur 为 p 的左孩子,进行右单旋
p 为 g 的右孩子,cur 为 p 的右孩子,进行左单旋
p 为 g 的左孩子,cur 为 p 的右孩子,进行左右双旋
p 为 g 的右孩子,cur 为 p 的左孩子,进行右左双旋
在这里插入图片描述

这里默认 a,b,c 都是含有一个黑色节点的红黑树
图不好画,大概按这样的过程理解
旋转完成后,还要变色,主要的变色逻辑就是上面展示的。
单旋:p 变为 黑,g 变为 红色
双旋:g 变为红, cur 变为黑
旋转的大概逻辑了解以后,下面开始实现

while(parent && parent->_parent && parent->_col == RED)
{Node* grandfather = parent->_parent;if(parent == grandfather->_left){Node* uncle = grandfather->_right;if(uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if(cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if(uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather-_col = RED;cur = grandfather;parent = cur->_parent;}else{if(cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col =RED;}}}_root->_col = BLACK;
}

左旋和右旋还是 AVL 树那套,但是要注意,这里我们在 insert 里修改节点颜色,所以左旋右旋里不需要修改颜色,同时 也不需要我们去单独写函数实现 左右双旋和右左双旋来修改颜色。

   void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL){subRL->_parent = parent;}if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){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 (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}

2.2.2. 插入全代码

	bool insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* cur = _root;Node* parent = nullptr;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;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL){subRL->_parent = parent;}if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){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 (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}

2.3. check

现在插入的大概逻辑是没问题了,但是和 AVL 树的问题一样,怎么样证明我们的树是 红黑树?
树是不是红黑树,我们需要根据红黑树的性质来判断
在这里插入图片描述
第 1 点,第 2 点,第 5 点都很好检查。
第 3 点相对来说就麻烦了
如果是红色节点,检查子节点是不是都是黑的,但是也不好检查,红色节点的孩子节点可能是 两个,可能是 一个,也可能不存在,所以我们可以考虑不向下查找,向上查找

bool Check(Node* root)
{if(root == nullptr){return true;}if(root->_col == RED){}return Check(root->_left) && Check(root->_right);
}
bool IsBalance(Node* root)
{if(root->_col ==BLACK){return true;}if(root->_col == RED){return false;}return Check(root);
}

IsBalance 检查根节点是不是黑色,Check 检查每个红色节点是不是有两个黑色子节点,如果直接检查红色节点的子节点情况,有点麻烦,所以我们反向检查

if(root->_col == RED && root->_parent->_col == RED)
{cout << "有连续的红色节点" << endl;return false;
}

这里的判断条件可以这样写,当前节点为红色,且这个节点的父节点是红色,就返回 false;
红色节点一定有父亲,且红色节点的父亲一定是黑色。
接下来处理,每条路径上的黑色节点数量相同,该怎么操作?
初步想法,拿栈可以检查,黑色节点入栈,当需要出栈时检测一下数量即可。
除了这个方法,我们还有其他方法
isBalance 传入 blacknum ,当遇见黑色节点++,直到遇见空节点时输出 blacknum 。

bool Check(Node* root, int blacknum)
{if(root == nullptr){cout << blacknum << "  ";}if(root->_col == RED & root->_parent->_col == RED){cout << "有连续的红色节点" << endl;}if(root->_col == BLACK){++blacknum;}return Check(root->_left, blacknum) && Check(root->_right, blacknum);
}
bool IsBalance()
{return _IsBalance(_root);
}
private:
bool _IsBalance(Node* root)
{if(root->_col == RED){return false;}int blacknum = 0;return Check(root, blacknum);
}

在这里插入图片描述
这里我们看见,所有路径的黑色节点数量都是 2
但是数据量太少了,我们需要和 AVL 树一样,传入大量数据来测试我们的 红黑树

void test_RBT ree2()
{const size_t N = 10000;vector<int> v1;RBTree<int, int> t1;srand(time(0));for(int i = 0; i < N; i++){v1.push_back(rand());}for(auto : v1){t1.insert(make_pair(e, e));}cout << t1.IsBalance() << endl;
}

在这里插入图片描述
当我们输出所有路径的黑色节点数量时,发现,这数据有点多,我们看不过来,不方便检查。
这里我们的思路是,除了传入 blacknum, 我们还需要传入一个判断的值

        bool Check(Node* root, int blacknum, const int refVal){if (root == nullptr){if (blacknum != refVal){cout << "存在黑色节点数量不相同的路径" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "有连续的红色节点" << endl;return false;}if (root->_col == BLACK){++blacknum;}return Check(root->_left, blacknum) && Check(root->_right, blacknum);}bool IsBalance(){return _IsBalance(_root);}
private:bool _IsBalance(Node* root){if (root->_col == RED){return false;}int blacknum = 0;int refVal = 0;Node* cur = root;while (cur){if (cur->_col = BLACK){refVal++;}cur = cur->_left;}return Check(root, blacknum, refVal);}

在这里插入图片描述
这里我们看见,在debug 版本下,插入和检查的时间一共用了 309ms,非常快
在这里插入图片描述
release 版本下,用了 119ms
在这里插入图片描述
100w 的数据一共是 18 层
注:这里看起来和 AVL 树高度差不多,其实是因为 rand 是伪随机,给的数值在量很大的情况下,会出现重复,所以表面上我们插入了 100w 个数据,实际上数据早就饱和了

2.4. erase

删除节点
删除的情况比插入的情况多很多,和AVL树一样,我们还是简单说一下,不实现
在这里插入图片描述
如果我们要删除 红色节点
在这里插入图片描述
这没啥影响,那5条规则没有受到破坏
如果删除的黑色节点
在这里插入图片描述
删除 黑色节点,我们发现,这个节点所在的路径黑色节点减少了,这里仅靠变色无法解决
在这里插入图片描述
25变红,17变黑?但是这样会导致出现连续的红色节点。
最右边的两条路径不管怎么样都会含有一个黑色节点,但是左边这条路径不存在黑色节点,所以需要旋转解决,这里还好看一点,右边整体高,右右左单旋。
在这里插入图片描述
删除时,如果出现问题,和插入一样,优先考虑 变色,后考虑旋转,但是旋转一定要放在 变色前。
删除的情况比较多,这里就不细说了

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

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

相关文章

初识webpack项目

新建一个空的工程 -> % mkdir webpack-project 为了方便追踪执行每一个命令&#xff0c;最终产生了哪些变更&#xff0c;将这个空工程初始化成git项目 -> % cd webpack-project/-> % git init Initialized empty Git repository in /Users/lixiang/frontworkspace/…

AI文章框架分析

大家在文章写作的时候结构难免会有点凌乱&#xff0c;但是自己可能无法发现问题所在&#xff0c;那么有没有一款工具可以帮你自动分析你写的文章框架存在的问题&#xff0c;然后并给你详细的分析报告呢&#xff1f;今天给大家介绍一下文件框架分析助手&#xff01; 使用说明 打…

如何配置X86应用程序启用大地址模式(将用户态虚拟内存从2GB扩充到3GB),以解决用户态虚拟内存不够用问题?(项目实战案例解析)

目录 1、概述 2、为什么不直接将程序做成64位的&#xff1f; 3、进程内存不足导致程序发生闪退的案例分析 3.1、问题说明 3.2、将Windbg附加到程序进程上进行动态调试 3.3、动态调试的Windbg感知到了中断&#xff0c;中断在DebugBreak函数调用上 3.4、malloc或new失败的…

IoTDB 入门教程 问题篇②——RPC远程连接IoTDB服务器失败

文章目录 一、前文二、发现问题三、分析问题四、检查6667端口是否监听所有IP五、检查ECS云服务器的安全组是否允许六、检查Linux防火墙是否允许 一、前文 IoTDB入门教程——导读 二、发现问题 使用本地IP127.0.0.1可以连接IoTDB服务器使用远程IPxx.xx.xx.xx却连接不到。提示你…

C++中的reverse_iterator迭代器结构设计

目录 reverse_iterator迭代器结构设计 reverse_iterator迭代器基本结构设计 operator*()函数 operator()函数 operator->()函数 operator!()函数 rbegin()函数 rend()函数 operator--()函数 operator()函数 测试代码 const_reverse_iterator迭代器设计 reverse…

【蓝桥2025备赛】容斥原理

容斥原理 背景&#xff1a;两个集合相交 高中的韦恩图&#xff0c;我们知道两个集合相交时我们可以通过简单的计算来认识相关的性质 集合相交的区域是 A ∩ B A\cap B A∩B ,集合的并集是 A ∪ B A\cup B A∪B ,那怎么用集合表示 A ∪ B A\cup B A∪B 我们可以看作是A集合…

分布式与一致性协议之ZAB协议(一)

ZAB协议 概述 很多人应该都使用过ZooKeeper&#xff0c; 它是一个开源的分布式协调服务&#xff0c;比如你可以用它进行配置管理、名字服务等。在ZooKeeper中&#xff0c;数据是以节点的形式存储的。如果你要用ZooKeeper做配置管理&#xff0c;那么就需要在里面创建指定配置&…

2024.5.5 机器学习周报

目录 引言 Abstract 文献阅读 1、题目 2、引言 3、创新点 4、匹配问题 5、SuperGlue架构 5.1、注意力图神经网络&#xff08;Attentional Graph Neural Network&#xff09; 5.2、最佳匹配层&#xff08;Optimal matching layer&#xff09; 5.3、损失 6、实验 6.…

模型剪枝——Linear Combination Approximation of Feature for Channel Pruning

线性逼近剪枝代码实现见文末 论文地址:CVPR 2022 Open Access Repositoryhttps://openaccess.thecvf.com/content/CVPR2022W/ECV/html/Joo_Linear_Combination_Approximation_of_Feature_for_Channel_Pruning_CVPRW_2022_paper.html 1.概述 传统的剪枝技术主要集中在去除对…

4.【Orangepi Zero2】Linux定时器(signal、setitimer),软件PWM驱动舵机(SG90)

Linux定时器&#xff08;signal、setitimer&#xff09;&#xff0c;软件PWM驱动舵机&#xff08;SG90&#xff09; signalsetitimer示例 软件PWM驱动舵机&#xff08;SG90&#xff09; signal 详情请看Linux 3.进程间通信&#xff08;shmget shmat shmdt shmctl 共享内存、si…

经纬度聚类:聚类算法比较

需求&#xff1a; 将经纬度数据&#xff0c;根据经纬度进行聚类 初始数据 data.csv K均值聚类 简介 K均值&#xff08;K-means&#xff09;聚类是一种常用的无监督学习算法&#xff0c;用于将数据集中的样本分成K个不同的簇&#xff08;cluster&#xff09;。其基本思想是…

支付宝支付流程

第一步前端&#xff1a;点击去结算&#xff0c;前端将商品的信息传递给后端&#xff0c;后端返回一个商品的订单号给到前端&#xff0c;前端将商品的订单号进行存储。 对应的前端代码&#xff1a;然后再跳转到支付页面 // 第一步 点击去结算 然后生成一个订单号 // 将选中的商…

Django之单文件上传(以图片为例)

一&#xff0c;创建项目 初始化&#xff0c;数据迁移&#xff0c;创建superuser&#xff0c;创建app等 二&#xff0c;配置settings.py 1&#xff0c;配置数据库&#xff08;本作者使用的mysql&#xff09;&#xff0c;以前文章有提到 2&#xff0c;配置静态文件存放路径 STAT…

2-手工sql注入(进阶篇) sqlilabs靶场5-10题

1. 阅读&#xff0c;学习本章前&#xff0c;可以先去看看基础篇&#xff1a;1-手工sql注入(基础篇)-CSDN博客 2. 本章通过对sqlilabs靶场的实战&#xff0c;关于sqlilabs靶场的搭建&#xff1a;Linux搭建靶场-CSDN博客 3. 本章会使用到sqlmap&#xff0c;关于sqlmap的命令&…

Vitis HLS 学习笔记--HLS流水线基本用法

目录 1. 简介 2. 示例 2.1 对内层循环打拍 2.2 对外层循环打拍 2.3 优化数组访问后打拍 3. 总结 1. 简介 本文介绍pipeline的基本用法。pipeline是一种用于提高硬件设计性能的技术。本文介绍了pipeline在累加计算函数中的应用。通过优化内外层循环和数组访问&#xff0c…

C++初学者,使用汉语编程

现在的IDE是完全支持中文编程的&#xff0c;对于C语系的爱好者来说&#xff0c;又可以发挥自己的想象力了。 今天使用一些宏定义写了一个小程序&#xff0c;用于玩弄C.

合合信息embedding模型登顶MTEB中文榜单:中文文本向量化技术的创新突破

引言MTEB中文榜单&#xff1a;权威性与挑战并存Embedding&#xff1a;特征与优势凸显模型应用&#xff1a;开启文本智能新篇章升级迭代&#xff1a;攻克行业技术难点结尾 引言 在信息化时代&#xff0c;文本数据呈爆炸式增长&#xff0c;如何高效、准确地处理和分析这些文本数…

Vue3-element-plus表格

一、element-plus 1.用组件属性实现跳转路由 <el-menu active-text-color"#ffd04b" background-color"#232323" :default-active"$route.path" //高亮 text-color"#fff"router><el-menu-item index"/article/channe…

【算法刷题日志】吸氧羊的StarryCoding之旅 - 贡献法计算

题目链接&#xff1a;https://www.starrycoding.com/problem/3 题目描述 吸氧羊终于注册了一个StarryCoding账号&#xff01;&#xff08;她很开心&#xff09; 但是吸氧羊忘记了它的密码&#xff0c;她想起你是计算机大师&#xff0c;于是就来请教你。 她虽然不记得密码了…

C++ | Leetcode C++题解之第70题爬楼梯

题目&#xff1a; 题解&#xff1a; class Solution { public:int climbStairs(int n) {double sqrt5 sqrt(5);double fibn pow((1 sqrt5) / 2, n 1) - pow((1 - sqrt5) / 2, n 1);return (int)round(fibn / sqrt5);} };