C++中红黑树的实现

目录

1.红黑树的概念

1.1红黑树的规则

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

1.3红黑树的效率

2.红黑树的实现 

2.1红黑树的结构

2.2红黑树的插入 

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

2.2.2情况1:叔叔节点存在且为红 -- 变色

2.2.3情况2:叔叔节点存在且为黑或者不存在 -- 旋转 + 变色

2.2.3.1情况2-1:c插入在p的左边 -- 右单旋 + 变色

2.2.3.2情况2-2:c插入在p的右边 -- 左右双旋 + 变色

2.2.4红黑树插入的总结

2.3红黑树的插入代码实现 

2.4红黑树的查找

2.5红黑树的验证 

3.红黑树和AVL树的性能检测

4.参考代码

4.1RBTree.h 

4.2AVLTree.h 

4.3测试代码Test.cpp


1.红黑树的概念

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

1.1红黑树的规则

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

        2.根节点是黑色的。

        3.如果一个节点是红色的,则它的两个孩子节点必须是黑色的,也就是说任何一条路径不会有连续的红色节点。

        4.对于任意一个节点,从该节点到其所有NULL节点的简单路径上,均包含相同数量的黑色节点。

        说明:《算法导论》等书籍上补充了⼀条每个叶⼦结点(NIL)都是⿊⾊的规则。他这⾥所指的叶⼦结点不是传统的意义上的叶⼦结点,⽽是我们说的空结点,有些书籍上也把NIL叫做外部结点。

        这里先看看几颗红黑树: 

 1.2红黑树如何确保最长路径不超过最短路径的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 ≈ logN ,也就是意味着红⿊树增删查改最坏也就是⾛最⻓路径 2 ∗ logN ,那么时间复杂度还是O(logN)

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

2.红黑树的实现 

2.1红黑树的结构

//枚举表示颜色
enum color
{RED,BLACK
};//默认按key/value结构实现
template <class K, class V>
class RBTreeNode
{//这里更新控制平衡也要加入parent指针pair<K, V> _kv;RBTreeNode _left;RBTreeNode _right;RBTreeNode _parent;color _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://...
private:Node _root = nullptr;
};

2.2红黑树的插入 

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

        1.插入一个值按二叉搜索树的规则进行插入(默认新插入的节点为红色),插入后观察是否符合红黑树的4条规则。

        2.如果是对空树进行插入,新增节点为根节点,将颜色改为黑色;如果是对非空树插入,新增节点必须为红色节点,如果为黑色节点则破环了规则4,规则4是很难维护的。

        3.非空树插入后,新增结点必须红⾊结点,如果⽗亲结点是⿊⾊的,则没有违反任何规则,插⼊结束。

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

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

2.2.2情况1:叔叔节点存在且为红 -- 变色

        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的左还是右,都是上⾯的变⾊处理⽅式。

        下图进行了抽象表达,d/e/f代表每条路径上拥有hb各黑色节点的子树,a/b代表每条路径拥有hb-1个黑色节点的根为红的子树,hb>=0。 

2.2.3情况2:叔叔节点存在且为黑或者不存在 -- 旋转 + 变色

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

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

        下面以分析叔叔节点在父亲节点的右边为例(叔叔节点在父亲节点的左边是类似的)。

2.2.3.1情况2-1:c插入在p的左边 -- 右单旋 + 变色

        如果p是g的左,c是p的左,那么以g为旋转点进行右单旋, 再把p变黑,g变红即可。p变成这棵树新的根,这样子树黑色节点的数量不变,没有连续的红色节点,且不需要往上更新,因为p为黑色,不管p的父亲节点是黑色还是红色都不违反规则。

2.2.3.2情况2-2:c插入在p的右边 -- 左右双旋 + 变色

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

2.2.4红黑树插入的总结

2.3红黑树的插入代码实现 

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;elseparent->_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 = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上更新cur = grandfather;parent = cur->_parent;}else    //叔叔不存在或者存在且为黑色{if (cur == parent->_left)	//插入到p的左边{RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else if (cur == parent->_right)	//插入到p的右边{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}else{return false;}break;}}else if (parent == grandfather->_right)	//叔叔在左边{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){//变色parent->_col = BLACK;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 if (cur == parent->_left){RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}else{return false;}break;}}else{return false;}}_root->_col = BLACK;	//最后把根节点的颜色改为黑色return true;}

2.4红黑树的查找

        红黑树的查找等同于二叉搜索树的查找。

	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.5红黑树的验证 

        这里获取最长路径和最短路径检查最长路径不超过最短路径的2倍是不可行的,因为就算满足这个给条件,红黑树也可能颜色不满足规则。所有还是要去检查红黑树的4点规则。

        规则1枚举颜色类型,天然满足要求。

        规则2直接检查根即可。

        规则3前序遍历检查,遇到红色节点检查孩子节点还需要判断孩子节点是否存在不太方便,所有反过来检查,遍历到孩子节点,去检查父亲节点的颜色是否为红色。

        规则4前序遍历,遍历过程中用形参记录根到当前节点的blackNum(黑色节点数量),前序遍历遇到黑色节点就++blackNum,走到空就计算出一条路径上的黑色节点数量。再以任意一条路径的黑色节点数量作为参考值,依次比较即可。

public:bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)    //检查规则2return 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)    //检查规则4{if (refNum != blackNum){cout << "存在黑色节点的数量不相等的路径" << endl;return false;}return true;}//遍历到孩子节点反过来检查父亲节点是不是连续的红色节点if (root->_col == RED && root->_parent->_col == RED)    //检查规则3{cout << root->_kv.first << "存在连续的红色节点" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);}

3.红黑树和AVL树的性能检测

        测试代码

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
using namespace std;#include "RBTree.h"
#include "AVLTree.h"void TestTree2()
{const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);}size_t begin1 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert({ e, e });}size_t end1 = clock();size_t begin2 = clock();RBTree<int, int> rbt;for (auto e : v){rbt.Insert({ e, e });}size_t end2 = clock();cout << "AVL Insert:" << end1 - begin1 << endl;cout << "RB Insert:" << end2 - begin2 << endl;cout << "AVL IsBalance:" << t.IsBalanceTree() << endl;cout << "RB IsBalance:" << rbt.IsBalance() << endl;cout << "AVL Height:" << t.Height() << endl;cout << "RB Height:" << rbt.Height() << endl;cout << "AVL Size:" << t.Size() << endl;cout << "RB Size:" << rbt.Size() << endl;size_t begin11 = clock();//确定的值for (auto e : v){t.Find(e);}随机值//for (size_t i = 0; i < N; i++)//{//	t.Find(rand() + i);//}size_t end11 = clock();size_t begin22 = clock();//确定的值for (auto e : v){rbt.Find(e);}随机值//for (size_t i = 0; i < N; i++)//{//	rbt.Find(rand() + i);//}size_t end22 = clock();cout << "AVL Find:" << end11 - begin11 << endl;cout << "RB Find:" << end22 - begin22 << endl;
}int main()
{TestTree2();return 0;
}

        插入100万个数据可以看出,红黑树和AVL虽然高度不同,但是插入和查找的速率是差不多的。 

4.参考代码

4.1RBTree.h 

#pragma once//枚举表示颜色
enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{//这里更新控制平衡也要加入parent指针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: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;elseparent->_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 = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上更新cur = grandfather;parent = cur->_parent;}else    //叔叔不存在或者存在且为黑色{if (cur == parent->_left)	//插入到p的左边{RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else if (cur == parent->_right)	//插入到p的右边{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}else{return false;}break;}}else if (parent == grandfather->_right)	//叔叔在左边{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){//变色parent->_col = BLACK;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 if (cur == parent->_left){RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}else{return false;}break;}}else{return false;}}_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;Node* pParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (pParent == nullptr){_root = subL;_root->_parent = nullptr;}else{if (pParent->_left == parent){pParent->_left = subL;}else{pParent->_right = subL;}subL->_parent = pParent;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//改变指针的指向parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* pParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (pParent == nullptr){_root = subR;_root->_parent = nullptr;}else{if (pParent->_left == parent){pParent->_left = subR;}else{pParent->_right = subR;}subR->_parent = pParent;}}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;}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)	//检查规则2return 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)	//检查规则4{if (refNum != blackNum){cout << "存在黑色节点的数量不相等的路径" << endl;return false;}return true;}//遍历到孩子节点反过来检查父亲节点是不是连续的红色节点if (root->_col == RED && root->_parent->_col == RED)	//检查规则3{cout << root->_kv.first << "存在连续的红色节点" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}int _Height(Node* root){if (root == nullptr)return 0;int left_height = _Height(root->_left);int right_height = _Height(root->_right);return left_height > right_height ? left_height + 1 : right_height + 1;}int _Size(Node* root){if (root == nullptr)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}Node* _root = nullptr;
};

4.2AVLTree.h 

        参考C++AVL树的介绍和实现中的参考代码。

4.3测试代码Test.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
using namespace std;#include "RBTree.h"
#include "AVLTree.h"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){t.Insert({ e, e });}t.InOrder();cout << t.IsBalance() << endl;
}void TestTree2()
{const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);}size_t begin1 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert({ e, e });}size_t end1 = clock();size_t begin2 = clock();RBTree<int, int> rbt;for (auto e : v){rbt.Insert({ e, e });}size_t end2 = clock();cout << "AVL Insert:" << end1 - begin1 << endl;cout << "RB Insert:" << end2 - begin2 << endl;cout << "AVL IsBalance:" << t.IsBalanceTree() << endl;cout << "RB IsBalance:" << rbt.IsBalance() << endl;cout << "AVL Height:" << t.Height() << endl;cout << "RB Height:" << rbt.Height() << endl;cout << "AVL Size:" << t.Size() << endl;cout << "RB Size:" << rbt.Size() << endl;size_t begin11 = clock();//确定的值for (auto e : v){t.Find(e);}随机值//for (size_t i = 0; i < N; i++)//{//	t.Find(rand() + i);//}size_t end11 = clock();size_t begin22 = clock();//确定的值for (auto e : v){rbt.Find(e);}随机值//for (size_t i = 0; i < N; i++)//{//	rbt.Find(rand() + i);//}size_t end22 = clock();cout << "AVL Find:" << end11 - begin11 << endl;cout << "RB Find:" << end22 - begin22 << endl;
}int main()
{//TestRBTree1();TestTree2();return 0;
}

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

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

相关文章

若依部署上线遇到的问题

一、若依部署上线的用户头像模块不能回显&#xff1a; 首先是后端修改部署上线后若依存储图片的本地地址 其次将上线前端配置文件中的图片相关配置给删除 二、若依部署上线后验证码不显示问题 在确保前后端请求打通后还有这个问题就是磁盘缓存问题 三、若依部署上线遇到404页…

生成式 AI 与向量搜索如何扩大零售运营:巨大潜力尚待挖掘

在竞争日益激烈的零售领域&#xff0c;行业领导者始终在探索革新客户体验和优化运营的新途径&#xff0c;而生成式 AI 和向量搜索在这方面将大有可为。从个性化营销到高效库存管理&#xff0c;二者在零售领域的诸多应用场景中都展现出变革性潜力&#xff0c;已成为保持行业领先…

【前端】css样式

文章目录 1.常用样式记录 1.常用样式记录 支持文字换行 white-space:pre-wrap;

WPF+Mvvm案例实战(五)- 自定义雷达图实现

文章目录 1、项目准备1、创建文件2、用户控件库 2、功能实现1、用户控件库1、控件样式实现2、数据模型实现 2、应用程序代码实现1.UI层代码实现2、数据后台代码实现3、主界面菜单添加1、后台按钮方法改造&#xff1a;2、按钮添加&#xff1a;3、依赖注入 3、运行效果4、源代码获…

102. UE5 GAS RPG 实现范围技能奥术伤害

在上一篇文章里&#xff0c;我们在技能蓝图里实现了通过技能实现技能指示&#xff0c;再次触发按键后&#xff0c;将通过定时器触发技能效果表现&#xff0c;最多支持11个奥术个体效果的播放。 在这一篇里&#xff0c;我们将实现技能播放时&#xff0c;对目标敌人应用技能伤害。…

C++11新特性相关内容详细梳理

0. 引言 C11简介&#xff1a; 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中的漏洞进行修复&#xff0c;语言的核心部分则没有改动&#xff0c;因此人们习惯性…

C#实现简单的文件夹对比程序

python版本的文件夹对比程序虽然简单&#xff0c;但可视化效果一般&#xff0c;不太好看。使用C#的Winform项目实现可视化对比文件夹内容&#xff0c;主要功能包括&#xff1a;   1&#xff09;采用Directory.GetDirectories获取子文件夹集合&#xff0c;Directory.GetFiles获…

C语言[求x的y次方]

C语言——求x的y次方 这段 C 代码的目的是从用户输入获取两个整数 x 和 y &#xff0c;然后计算 x 的 y 次幂&#xff08;不过这里有个小错误&#xff0c;实际计算的是 x 的 (y - 1) 次幂&#xff0c;后面会详细说&#xff09;&#xff0c;最后输出结果。 代码如下: #include…

8 个用于创建电商组件的 CSS 和 JS 代码片段

文章目录 前言正文1.自定义办公桌配置工具2.商品展示卡片3.Vue.js 支持的便捷购物体验4.简化的多步结账流程5.移动端优化的商品页面6.动态购物车效果7.React 支持的购物车页面8.尺码指南 总结 前言 优秀的电商网站&#xff0c;必须操作简便、注重细节&#xff0c;才能让用户留…

飞书文档解除复制限制

解除飞书文档没有编辑器权限限制复制功能方法 方法一&#xff1a;使用插件 方法二&#xff1a; 通过调试工具删除所有的copy事件 使用插件 缺点&#xff1a; 只有markdown格式&#xff0c;如果需要其他格式需要再通过Typora等markdown编辑器转pdf,word等格式 安装插件 Cloud Do…

OpenTelemetry 实际应用

介绍 OpenTelemetry“动手”指南适用于想要开始使用 OpenTelemetry 的人。 如果您是 OpenTelemetry 的新手&#xff0c;那么我建议您从OpenTelemetry 启动和运行帖子开始&#xff0c;我在其中详细介绍了 OpenTelemetry。 OpenTelemetry开始改变可观察性格局&#xff0c;它提供…

AAPL: Adding Attributes to Prompt Learning for Vision-Language Models

文章汇总 当前的问题 1.元标记未能捕获分类的关键语义特征 如下图(a)所示&#xff0c; π \pi π在类聚类方面没有显示出很大的差异&#xff0c;这表明元标记 π \pi π未能捕获分类的关键语义特征。我们进行简单的数据增强后&#xff0c;如图(b)所示&#xff0c;效果也是如…

RestHighLevelClient操作es查询文档

目录 利用RestHighLevelClient客户端操作es查询文档 查询match_all dsl语句&#xff1a; ​编辑 java代码 小结 match字段全文检索查询 dsl语句 java代码 multi_match多字段全文检索查询 dsl语句 java代码 term精确查询 dsl语句 java代码 range范围查询 dsl语句 j…

鸿蒙是必经之路

少了大嘴的发布会&#xff0c;老实讲有点让人昏昏入睡。关于技术本身的东西&#xff0c;放在后面。 我想想来加把油~ 鸿蒙发布后褒贬不一&#xff0c;其中很多人不太看好鸿蒙&#xff0c;一方面是开源性、一方面是南向北向的利益问题。 不说技术的领先点&#xff0c;我只扯扯…

破解API加密逆向接口分析,看这篇就够了

破解API加密逆向接口分析&#xff0c;看这篇就够了 每日一练&#xff1a;API接口数据逆向&#xff0c;看完这篇&#xff0c;就能学会找到逆向的入口函数、调试js代码、分析js代码、还原加解密算法&#xff01;为了能及时获取后续的爬虫及逆向的技术分享文章&#xff0c;请先关注…

qt EventFilter用途详解

一、概述 EventFilter是QObject类的一个事件过滤器&#xff0c;当使用installEventFilter方法为某个对象安装事件过滤器时&#xff0c;该对象的eventFilter函数就会被调用。通过重写eventFilter方法&#xff0c;开发者可以在事件处理过程中进行拦截和处理&#xff0c;实现对事…

代码随想录算法训练营第46期

class Solution { public: // 决定dp[i]的因素就是第i房间偷还是不偷。 // 偷第i房间&#xff0c;那么dp[i] dp[i - 2] nums[i] 即&#xff1a;第i-1房一定是不考虑的&#xff0c;找出 下标i-2&#xff08;包括i-2&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[…

Unity插件-Intense TPS 讲解

目录 关于TPS 打开场景&#xff1a;WeaponTest.unity&#xff0c; 只要把这些枪点&#xff0c;打开&#xff08;默认隐藏&#xff0c;不知道为何), 一开始不能运行如何修复 总结 关于TPS 个人不是TPS&#xff0c;FPS的射击游戏爱好者&#xff0c; 不过感觉这个枪感&…

riscv uboot 启动流程分析 - SPL启动流程

分析uboot 启动流程硬件&#xff1a;启明智显推出M4核心板 &#xff08;https://gitee.com/qiming-zhixian/m4-openwrt&#xff09; 1.U-boot和SPL概述 U-Boot 分为 uboot-spl 和 uboot 两个组成部分。SPL 是 Secondary Program Loader 的简称&#xff0c;第二阶段程序加载器。…

springboot083基于springboot的个人理财系统--论文pf(论文+源码)_kaic

基于springboot的个人理财系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了个人理财系统的开发全过程。通过分析个人理财系统管理的不足&#xff0c;创建了一个计算机管理个人理财系统的方案。文章介绍了个…