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

【C++】map和set的模拟实现

1.底层红黑树节点的定义

enum Colur
{RED,BLACK
};
template<class T> 
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colur _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr),_data(data),_col(RED){ }
};

只需要一个模板参数就可以实现map和set,T可以接收key作为set,也可以接收一个键值对作为map。map和set用的同一个类模板,通过不同的模板参数实现不同的类

2.底层红黑树迭代器的实现

typedef重定义

在这里插入图片描述
1.Self: 表示当前实例化的 _TreeIterator 类型,其指针类型和引用类型由模板参数 Ptr 和 Ref 决定。这使得 Self 可以适用于不同的指针和引用类型,通常用于类内部实现,例如在成员函数中返回或传递迭代器时,保持与当前迭代器类型一致。。
2.terator: 表示一个具体的 _TreeIterator 类型,其中指针类型是 T*,引用类型是 T&。这是一个特定的迭代器类型通常用于提供给用户的标准接口,例如在容器类中提供 begin 和 end 方法返回这种类型的迭代器。。

构造函数

在这里插入图片描述
1.是关键,由上图定义可知,iterator一直是一个普通迭代器。有以下两种情况A:当前类被实例化为const迭代器时,这是一个构造函数支持普通迭代器构造const迭代器,将普通迭代器 it 的节点指针 it.node 赋值给当前 const 迭代器的 node 成员变量。
B:当前类被实例化为普通迭代器时,该函数为拷贝构造
作用: 允许 _TreeIterator 类型的迭代器对象能够从另一个 Iterator 类型的迭代器对象进行初始化,确保两者指向同一个节点。
2.node: 这是一个指向红黑树节点的指针。_node(node): 通过初始化列表将传入的节点指针赋给当前迭代器的 _node 成员变量。
作用: 允许直接通过一个节点指针初始化 _TreeIterator 对象,使得迭代器指向指定的节点。

重载前置++

在这里插入图片描述

Self& operator++()
{//右子树不为空访问其最左节点(最小)if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}//else//找孩子是父亲左的那个祖先节点,就是下一个要访问的节点//{//	Node* cur = _node;//	Node* parent = cur->_parent;//	while (parent)//	{//		if (parent->_left == cur)//		{//			break;//		}//		else//		{//			//继续往上更新节点//			cur = cur->_parent;//			parent = parent->_parent;//		}//	}//	_node = parent;//}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;
}

else语句有两种实现方式,第二种更为简洁清晰。

重载前置–

与++顺序相反,按右子树 根 左子树的顺序访问节点 。实现思路反过来即可:
1.左树不为空,访问左树的最右节点(最大节点)
2.左树为空,代表该子树已访问完成,访问孩子是父亲右的那个祖先节点

Self& operator--()
{if (_node->_left){Node* subright = _node->_left;while (subright->_right){subright = subright->_right;}_node = subright;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;
}

看一下库里面的实现方式:
在这里插入图片描述
相比我们自己的模拟实现,增加了一个哨兵位节点,与根节点双向链接,通过leftmost函数找到左树中的最右节点,结束标志即返回哨兵位节点为空。这样在查找下一个访问的节点时通过调用函数固然很方便,但在旋转维护红黑树性质时增加了一定的复杂性。
自己模拟实现时通过while (parent && cur == parent->_left)该条件加上一个哨兵位节点也可以实现库中方式,该循环条件避免了cur和parent的无限循环错误

基本操作

在这里插入图片描述

3.底层红黑树的实现

通用模板

在这里插入图片描述
K:键类型,用于在红黑树中进行排序和查找
T:存储在红黑树中的元素类型。对于set,T就是K;对于映射map,T是一个键值对pair。
keyofT:一个函数对象,用于从元素类型T中提取键K。主要服务于map,对于映射map就是pair的第一个元素迭代器。set为了保持结构一致也用该函数,不过就直接返回key即可

获取迭代器的成员函数

//起始位置是树的最左节点,即最小节点
iterator begin()
{Node* leftmin = _root;while (leftmin&&leftmin->_left){leftmin = leftmin->_left;}//调用迭代器的构造函数return iterator(leftmin);
}iterator end()
{
//调用迭代器的构造函数return iterator(nullptr);
}const_iterator begin()const
{Node* leftmin = _root;while (leftmin && leftmin->_left){leftmin = leftmin->_left;}return const_iterator(leftmin);
}const_iterator end()const
{return const_iterator(nullptr);
}

查找节点

Node* Find(const K& key)
{Node* cur = _root;keyofT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else//相等找到了{return cur;}}return nullptr;
}

需注意与普通红黑树的比较方式不同,需调用kot来进行比较。
kot 的作用是从节点的数据中提取键值,使得红黑树的查找、插入和删除等操作能够基于键值进行比较和决策。这种设计使得红黑树模板具有通用性,可以适应不同的数据类型和键提取逻辑。

插入节点

与普通红黑树实现思路相同,只做出以下改变
1.通过kot提取节点数据的键值进行比较
2.返回值变成一个键值对,first为迭代器,second为bool值表示插入是否成功

pair<iterator,bool> Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root),true);}//查找插入位置Node* cur = _root;Node* parent = nullptr;//map和set共用模板,需要将data转化为键值keyofT kot;while (cur){if (kot(cur->_data)<kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data)>kot(data)){parent = cur;cur = cur->_left;}else{//找到相同键值return make_pair(iterator(cur), false);}}//插入新节点cur = new Node(data);cur->_col = RED;//cur要去遍历,需提前保存Node* newnode = cur;if (kot(parent->_data)>kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent&&parent->_col==RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//uncle存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新节点.继续向上处理cur= grandfather;//注意赋值的顺序parent = grandfather->_parent;}else//uncle不存在或为黑{//判断是单旋还是双旋if (cur == parent->_left){//	  g//	p//cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//    g//  p//	  cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else//parent == grandfather->_right{Node* uncle = grandfather->_left;//uncle存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新节点.继续向上处理cur= grandfather;parent = grandfather->_parent;}else//uncle不存在或为黑{//判断是单旋还是双旋if (cur == parent->_right){//g  //  p//    cRotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//g//  p//cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);
}

其余代码如单双旋,测试部分与i普通红黑树相同,详见文章末尾总体代码

4.map的模拟实现

map的底层结构就是红黑树,因此在map中直接封装一棵红黑树,然后将其接口包装下即可

#pragma once
#include"RBTree.h"
namespace ee
{template<class K,class V>class map{struct MapkeyofT{const K&operator() (const pair<K, V>&kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapkeyofT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapkeyofT>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin()const{return _t.begin();}const_iterator end()const{return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, c));return ret.first->second;}pair<iterator,bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapkeyofT> _t;};
}

解析:
1.map 类是一个模板类,K 表示键的类型,V 表示值的类型。
2.迭代器中pair<const K, V>保证了键值不可修改的同时能修改元素值,const迭代器就两个都不能修改
3.[]重载:提供了对指定键的值的访问。

默认构造值V():当键不存在时,operator[] 会插入一个默认构造的值。这意味着每次访问不存在的键时,都会自动创建一个新的键值对。
如果键已存在,返回已存在值的引用。

5.set的模拟实现

#pragma once
#include"RBTree.h"
namespace ee
{template<class K>class set{struct SetkeyofT{const K& operator()(const K&key){return key;}};public:typedef typename RBTree<K, K, SetkeyofT>::const_iterator iterator;typedef typename RBTree<K, K, SetkeyofT>::const_iterator const_iterator;//普通迭代器底层也是const迭代器,直接定义成constconst_iterator begin()const{return _t.begin();}const_iterator end()const{return _t.end();}pair<iterator,bool> insert(const K& key){return _t.Insert(key);}//pair<iterator, bool> insert(const K& key)//{//	// pair<RBTree::iterator, bool>//	pair<typename RBTree<K, K, SetkeyofT>::iterator, bool> ret = _t.Insert(key);//	return pair<iterator, bool>(ret.first, ret.second);//}private:RBTree<K, K, SetkeyofT> _t;};
}

解析:
与map不同的是,迭代器的定义const和非const迭代器的底层都为const迭代器,保证了键值的不可修改.但这样会引起插入时的问题

在这里插入图片描述
由于set模拟实现中定义的迭代器底层也是const迭代器,所以存在返回值不匹配的问题。_t是一个普通对象调用的是普通迭代器,所以我们指定返回底层红黑树中也就是普通迭代器然后再通过构造函数用普通迭代器构造const迭代器,如下图
在这里插入图片描述

6.整体代码

  • 底层红黑树改装
#pragma once
#include<iostream>
#include<vector>
using namespace std;enum Colur
{RED,BLACK
};
template<class T> 
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colur _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr),_data(data),_col(RED){ }
};template<class T,class Ptr,class Ref>
struct _TreeIterator
{typedef RBTreeNode<T> Node;typedef _TreeIterator<T,Ptr,Ref> Self;typedef _TreeIterator<T, T*, T&> Iterator;Node* _node;// 因为这里写了这个转换,所以可以从非 const iterator转化到const的iterator,// 如果注释掉了的话,代码就编译出错了_TreeIterator(const Iterator&it):_node(it._node){ }_TreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}//这里是重载前置++Self& operator++(){//右子树不为空访问其最左节点(最小)if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}//else//找孩子是父亲左的那个祖先节点,就是下一个要访问的节点//{//	Node* cur = _node;//	Node* parent = cur->_parent;//	while (parent)//	{//		if (parent->_left == cur)//		{//			break;//		}//		else//		{//			//继续往上更新节点//			cur = cur->_parent;//			parent = parent->_parent;//		}//	}//	_node = parent;//}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node->_left){Node* subright = _node->_left;while (subright->_right){subright = subright->_right;}_node = subright;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}
};// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<K, V>, MapKeyOfT> _t;
template<class K,class T,class keyofT>
struct RBTree
{typedef RBTreeNode<T> Node;
public:typedef _TreeIterator<T,T*,T&> iterator;typedef _TreeIterator<T,const T*,const T&> const_iterator;iterator begin(){Node* leftmin = _root;while (leftmin&&leftmin->_left){leftmin = leftmin->_left;}//调用迭代器的构造函数return iterator(leftmin);}iterator end(){return iterator(nullptr);}const_iterator begin()const{Node* leftmin = _root;while (leftmin && leftmin->_left){leftmin = leftmin->_left;}//调用迭代器的构造函数return const_iterator(leftmin);}const_iterator end()const{return const_iterator(nullptr);}Node* Find(const K& key){Node* cur = _root;keyofT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else//相等找到了{return cur;}}return nullptr;}pair<iterator,bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root),true);}//查找插入位置Node* cur = _root;Node* parent = nullptr;//map和set共用模板,需要将data转化为键值keyofT kot;while (cur){if (kot(cur->_data)<kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data)>kot(data)){parent = cur;cur = cur->_left;}else{//找到相同键值return make_pair(iterator(cur), false);}}//插入新节点cur = new Node(data);cur->_col = RED;//cur要去遍历,需提前保存Node* newnode = cur;if (kot(parent->_data)>kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent&&parent->_col==RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//uncle存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新节点.继续向上处理cur= grandfather;//注意赋值的顺序parent = grandfather->_parent;}else//uncle不存在或为黑{//判断是单旋还是双旋if (cur == parent->_left){//	  g//	p//cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//    g//  p//	  cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else//parent == grandfather->_right{Node* uncle = grandfather->_left;//uncle存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新节点.继续向上处理cur= grandfather;parent = grandfather->_parent;}else//uncle不存在或为黑{//判断是单旋还是双旋if (cur == parent->_right){//g  //  p//    cRotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//g//  p//cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);}void RotateL(Node* parent){_rotateCount++;Node* cur = parent->_right;Node* curleft = cur->_left;Node* ppnode = parent->_parent;//第一次改变链接parent->_right = curleft;if (curleft){curleft->_parent = parent;}//第二次改变链接cur->_left = parent;parent->_parent = cur;//判断根节点的链接情况//为根节点调整平衡因子情况if (parent == _root){_root = cur;cur->_parent = nullptr;}//树中的部分调整情况else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}void RotateR(Node* parent){_rotateCount++;Node* cur = parent->_left;Node* curright = cur->_right;Node* ppnode = parent->_parent;//第一次链接parent->_left = curright;if (curright){curright->_parent = parent;}//第二次链接cur->_right = parent;parent->_parent = cur;//调整根节点链接关系if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}bool CheckColur(Node* root, int blacknum, int benchmark){if (root == nullptr){if (blacknum != benchmark){return false;}return true;}//判断树中黑节点数量if (root->_col==BLACK){blacknum++;}//判断树中是否有连续红节点情况if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return CheckColur(root->_left, blacknum, benchmark)&& CheckColur(root->_right, blacknum, benchmark);}bool IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr){return true;}if (root->_col != BLACK){return false;}//计算基准值int benchmark = 0;Node* cur = root;while (cur){if(cur->_col==BLACK)benchmark++;//选择一条路径来计算黑节点数量cur = cur->_left;}return CheckColur(root, 0, benchmark);}int Height(){return Height(_root);}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;}bool IsBST() {vector<int> result;_Inorder(_root,result);for (int i = 1; i < result.size(); i++) {if (result[i] <= result[i - 1]) {return false;}}return true;}void _Inorder(Node* root, vector<K>& result) {if (root == nullptr) {return;}_Inorder(root->_left, result);result.push_back(root->_kv.first);_Inorder(root->_right, result);}public:int _rotateCount = 0;private:Node* _root = nullptr;
};
  • 测试代码
#include<iostream>
using namespace std;
#include"MyMap.h"
#include"MySet.h"int main()
{ee::set<int> s;auto iterator = s.insert(10);s.insert(24);s.insert(3);s.insert(4);s.insert(34);s.insert(1024);ee::set<int>::iterator it = s.begin();while (it != s.end()){/*if (*it % 2 == 0){*it += 10;}*/cout << *it <<" ";++it;}cout << endl;ee::map<int, int> m;m.insert(make_pair(1, 1));m.insert(make_pair(2, 2));m.insert(make_pair(3, 3));for (const auto& kv : m){cout << kv.first << ":" << kv.second << endl;}cout << endl;//测试map的[]重载ee::map<string, string>dict;dict.insert(make_pair("学习", "nice"));dict["study"];//插入for (const auto& kv : dict){cout << kv.first << " " << kv.second << endl;}cout << endl;dict["study"] = "好好学习";//修改dict["学习"] = "deserve";dict["mint"] = "薄荷";//插入+修改for (const auto& kv : dict){cout << kv.first << " " << kv.second << endl;}cout << endl;return 0;
}

相关文章:

【C++】map和set的模拟实现

1.底层红黑树节点的定义 enum Colur {RED,BLACK }; template<class T> struct RBTreeNode {RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colur _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr…...

数据结构·字典树

字典树trie 顾名思义&#xff0c;在一个字符串的集合里查询某个字符串是否存在树形结构。 树存储方式上用的是结构体数组&#xff0c;类似满二叉树的形式。 模板 定义结构体和trie 结构体必须的内容&#xff1a;当前结点的字符&#xff0c;孩子数组可选&#xff1a;end用于查…...

centos服务器,疑似感染phishing家族钓鱼软件的检查

如果怀疑 CentOS 服务器感染了 Phishing 家族钓鱼软件&#xff0c;需要立即进行全面检查并采取相应措施。以下是详细的检查和处理步骤&#xff1a; 1. 立即隔离服务器 如果可能&#xff0c;将服务器从网络中隔离&#xff0c;以防止进一步传播或数据泄露。如果无法完全隔离&…...

(C语言)超市管理系统(测试2版)(指针)(数据结构)(清屏操作)

目录 前言&#xff1a; 源代码&#xff1a; product.h product.c fileio.h fileio.c main.c 代码解析&#xff1a; 一、程序结构概述 二、product.c 函数详解 1. 初始化商品列表 Init_products 2. 添加商品 add_product 3. 显示商品 display_products 4. 修改商品 mo…...

可变形卷积简介(Deformable Convolution)

1. 核心原理 可变形卷积通过动态调整卷积核的采样位置&#xff0c;增强模型对几何形变&#xff08;如旋转、缩放&#xff09;的适应能力。其核心改进包括&#xff1a; 偏移量&#xff08;Offset&#xff09;&#xff1a;为卷积核的每个采样点学习 x / y x/y x/y方向的偏移量 …...

02_Servlet

目录 一、简介二、Servlet入门案例2.1、编写Servlet2.2、配置Servlet2.3、访问Servlet2.4、常见错误 三、Servlet详解3.1、实现Servlet的三种方式3.1.1、实现Servlet接口3.1.2、继承GenericServlet类3.1.3、继承HttpServlet类 3.2、配置Servlet的两种方式3.2.1、web.xml方式3.2…...

stm32之FLASH

目录 1.简介2.闪存模块组织3.基本结构3.1 FPEC3.2 程序存储器3.2.1 标准编程3.2.2 页擦除3.2.3 全擦除 3.3 选项字节3.3.1 编程3.3.2 擦除 4.器件电子签名5.实验-读取内部FLASH 1.简介 STM32F1系列的FLASH内存是一个非常重要的存储区域&#xff0c;它主要由三个部分组成&#…...

第3.4节 调用链路分析服务开发

3.4.1 什么是Code Call Graph&#xff08;CCG&#xff09; Code Call Graph&#xff08;CCG&#xff09;即业务代码中的调用关系图&#xff0c;是通过静态分析手段分析并构建出的一种描述代码间关系的图。根据精度不同&#xff0c;一般分为类级别、方法级别、控制流级别&#x…...

超详细Docker教程

前言&#xff1a;大家在在Linux上部署mysql及其他软件时&#xff0c;大家想一想自己最大的感受是什么&#xff1f; 我相信&#xff0c;除了个别天赋异禀的人以外&#xff0c;大多数人都会有相同的感受&#xff0c;那就是麻烦。核心体现在三点&#xff1a; 命令太多了&#xff…...

探索AI新领域:生成式人工智能认证(GAI认证)助力职场发展

在数字化时代的大潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术以其强大的影响力和广泛的应用前景&#xff0c;正逐步重塑我们的生活与工作方式。随着生成式AI技术的崛起&#xff0c;掌握这一前沿技能已成为职场竞争中的关键优势。那么&#xff0c;如何通过系统的学…...

sql sql复习

虽然之前学习过sql&#xff0c;但由于重在赶学习进度&#xff0c;没有学扎实&#xff0c;导致自己刷题的时候有的地方还是模模糊糊&#xff0c;现在主要是复习&#xff0c;补一补知识点。 今日靶场&#xff1a; NSSCTF 云曦历年考核题 在做题之前先回顾一下sql注入的原理&…...

初探 Skynet:轻量级分布式游戏服务器框架实战

在游戏服务器开发领域&#xff0c;高效、稳定且易于扩展的框架一直是开发者追求的目标。Skynet 作为一款轻量级、高性能的分布式游戏服务器框架&#xff0c;凭借其独特的设计理念和强大的功能&#xff0c;赢得了众多开发者的青睐 一.Skynet底层架构支持 1.Actor erlang 从语言…...

libarchive.so.19丢失

文章目录 绝对路径可以启动&#xff0c;相对路径不可以以绝对路径启动conda环境&#xff0c;运行python3.8(成功) 报错 Error while loading conda entry point: conda-libmamba-solver (libarchive.so.19: cannot open shared object file: No such file or directory) sudo a…...

vue-ganttastic甘特图label标签横向滚动固定方法

这个甘特图之前插件里&#xff0c;没有找到能固定label标签在屏幕上的办法&#xff0c;用css各种办法都没有实现&#xff0c;所以我我直接手写定位&#xff0c;用js监听滚动条滚动的距离&#xff0c;然后同步移动甘特图label标签&#xff0c;造成一种定位的错觉&#xff0c;以下…...

自动化 NuGet 包打包与上传:完整批处理脚本详解(含 SVN 支持)

在大型项目中&#xff0c;我们常常需要定期打包多个 .csproj 项目为 NuGet 包&#xff0c;并上传到私有 NuGet 服务。这篇文章分享一份实战脚本&#xff0c;支持以下自动化流程&#xff1a; 自动读取、更新 .csproj 文件中的 Version、PackageOutputPath 等节点&#xff1b; 自…...

Go语言空白导入的作用与用途

在 Go 语言中&#xff0c;导入包时前面加下划线 _ 是一种特殊的导入方式&#xff0c;称为 “空白导入” 或 “匿名导入”。 作用&#xff1a; 执行包的初始化&#xff08;init 函数&#xff09;但不直接使用包中的标识符 import _ "go.uber.org/automaxprocs" 表示你…...

实验六:按键模拟控制实现

FPGA序列检测器实验(远程实验系统) 文章目录 FPGA序列检测器实验(远程实验系统)一、数字电路基础知识1. 时钟与同步2. 按键消抖原理代码讲解:分频与消抖3. 有限状态机(FSM)设计代码讲解:状态机编码与转移4. 边沿检测与信号同步5. 模块化设计二、实验数字电路整体思想三…...

【愚公系列】《Manus极简入门》038-数字孪生设计师:“虚实映射师”

&#x1f31f;【技术大咖愚公搬代码&#xff1a;全栈专家的成长之路&#xff0c;你关注的宝藏博主在这里&#xff01;】&#x1f31f; &#x1f4e3;开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主&#xff01; &#x1f…...

Linux重定向与缓冲区

目录 文件描述符的分配规则 重定向 使用 dup2 系统调用 FILE 文件描述符的分配规则 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main() {int fd open("myfile", O_RDONLY);if(fd < 0){per…...

经典还原反应解析:Wolff-Kishner机制与黄鸣龙改进法

在有机化学发展史上记载的万余种经典反应中&#xff0c;当论及以科学家命名的标志性转化反应时&#xff0c;Wolff-Kishner-黄鸣龙还原反应必然占据重要地位。在大学《有机化学》课程中&#xff0c;学习还原反应时肯定会提到Wolff-Kishner-黄鸣龙还原反应&#xff0c;这是第一个…...

DataX从Mysql导数据到Hive分区表案例

0、下载DataX并解压到对应目录 DataX安装包&#xff0c;开箱即用&#xff0c;无需配置。 https://datax-opensource.oss-cn-hangzhou.aliyuncs.com/202308/datax.tar.gz 相关参考文档 https://github.com/alibaba/DataX/blob/master/hdfswriter/doc/hdfswriter.md 1、Hive分区…...

npm 报错 gyp verb `which` failed Error: not found: python2 解决方案

一、背景 npm 安装依赖报如下错&#xff1a; gyp verb check python checking for Python executable "python2" in the PATH gyp verb which failed Error: not found: python2 一眼看过去都觉得是Python环境问题&#xff0c;其实并不是你python环境问题&#xf…...

安装npm:npm未随Node.js一起安装

文章目录 上传至linux服务器/usr/local/目录下 如果npm没有随Node.js一起安装&#xff0c;你可以尝试单独下载并安装npm。但通常情况下&#xff0c;这是不必要的&#xff0c;因为npm是Node.js的一部分。如果确实需要单独安装npm&#xff0c;你可以参考npm的官方安装指南。 npm…...

C++23 ranges::to:范围转换函数 (P1206R7)

文章目录 引言C23 Ranges 概述ranges::to 的定义与功能定义功能 使用场景范围转换为容器简化字符串解析映射转换为向量 ranges::to 的优势代码简洁性提高开发效率与C23的stl容器的范围版本构造函数配合良好 模板参数约束的思考总结 引言 在C的发展历程中&#xff0c;每一个新版…...

openfeign 拦截器实现微服务上下文打通

目录 openfeign 拦截器实现微服务上下文打通需求分析&#xff1a;代码实现&#xff1a;subject 服务&#xff1a;controllerFeign 拦截器&#xff1a;将 Feign 拦截器注册为一个Bean&#xff1a; auth 鉴权服务&#xff1a;全局配置类&#xff1a;登录拦截器&#xff1a;上下文…...

【MySQL】变更缓冲区:作用、主要配置以及如何查看

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

TCP/IP-——C++编程详解

1. TCP/IP 编程基本概念 TCP&#xff08;传输控制协议&#xff09;&#xff1a;面向连接、可靠的传输层协议&#xff0c;保证数据顺序和完整性。IP&#xff08;网际协议&#xff09;&#xff1a;负责将数据包路由到目标地址。Socket&#xff08;套接字&#xff09;&#xff1a…...

微服务如何实现服务的高可用

背景&#xff1a;微服务分层需要考虑高可用和高并发的问题 微服务如何实现服务的高可用 先说结论&#xff1a;微服务实现高可用主要通过集群冗余故障自动转移来实现的具体可以从底下几种方案来实现。 “端”到“反向代理”的高可用“反向代理”到“站点应用”的高可用“站点…...

微服务调试问题总结

本地环境调试。 启动本地微服务&#xff0c;使用公共nacos配置。利用如apifox进行本地代码调试解决调试问题。除必要的业务微服务依赖包需要下载到本地。使用mvn clean install -DskipTests进行安装启动前选择好profile环境进行启动&#xff0c;启动前记得mvn clean清理项目。…...

egpo进行train_egpo训练时,keyvalueError:“replay_sequence_length“

def execution_plan(workers: WorkerSet, config: TrainerConfigDict) -> LocalIterator[dict]: if config.get(“prioritized_replay”): prio_args { “prioritized_replay_alpha”: config[“prioritized_replay_alpha”], “prioritized_replay_beta”: config[“prior…...

Hadoop的组成

&#xff08;一&#xff09;Hadoop的组成 对普通用户来说&#xff0c; Hadoop就是一个东西&#xff0c;一个整体&#xff0c;它能给我们提供无限的磁盘用来保存文件&#xff0c;可以使用提供强大的计算能力。 在Hadoop3.X中&#xff0c;hadoop一共有三个组成部分&#…...

Android锁

引言 &#x1f512; 在 Android 应用的开发过程中&#xff0c;随着业务需求的复杂度不断提升&#xff0c;多线程并发场景层出不穷。为了保证数据一致性与线程安全&#xff0c;锁&#xff08;Lock&#xff09;成为了不可或缺的工具。本篇博客将深入剖析 Android 中常用的锁机制…...

XD08M3232接近感应单片机的接近感应模块的工作原理

XD08M3232接近感应单片机的接近感应模块基于电容式感应原理&#xff0c;通过硬件电路与软件配置实现对物体接近的检测。以下是其工作原理的详细解析&#xff1a; 一、硬件架构与核心组件 1. 核心电路组成 接近感应模块由三大关键部分构成&#xff1a; 两个轨到轨运算放大器&…...

编程日志5.6

串的习题 1.2236. 判断根结点是否等于子结点之和 - 力扣(LeetCode) /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * Tr…...

量子计算实用化突破:从云端平台到国际竞合,开启算力革命新纪元

在硅谷某生物医药实验室&#xff0c;研究员艾米丽正盯着量子计算模拟界面露出微笑 —— 搭载中电信 "天衍" 量子计算云平台的 880 比特超导量子处理器&#xff0c;用 17 分钟完成了传统超算需 3 个月才能跑完的新型抗生素分子键合模拟。这个场景标志着量子计算正从 &…...

Made with Unity | 拓展“双点”宇宙版图

双点工作室&#xff08;Two Point Studios&#xff09;团队成立于2016年&#xff0c;其创立初衷是打造一个完美的游戏IP&#xff1a;构建一个既能持续吸引玩家&#xff0c;又具备足够扩展空间&#xff0c;同时经得起时间考验的虚拟世界。2018年&#xff0c;团队以《双点医院&am…...

3D 数据可视化系统是什么?具体应用在哪方面?

目录 一、3D 数据可视化系统的定义与内涵 &#xff08;一&#xff09;基本概念 &#xff08;二&#xff09;核心要素 二、3D 数据可视化系统的优势 三、3D 数据可视化系统的应用领域 &#xff08;一&#xff09;城市规划与管理 &#xff08;二&#xff09;工业制造 &am…...

2025-5-14渗透测试:利用Printer Bug ,NTLMv2 Hash Relay(中继攻击),CVE-2019-1040漏洞复现

python3 printerbug.py test.com/test192.168.186.131 192.168.186.134 sudo python2 MultiRelay.py -t 192.168.186.132 -u ALLPrinter Bug 原理 PrinterBug&#xff08;CVE-2018-0886&#xff09;是Windows打印系统服务&#xff08;Spooler&#xff09;的一个设计缺陷&…...

OracleLinux7.9-ssh问题

有套rac环境&#xff0c;db1主机无法ssh db1和db1-priv&#xff0c;可以ssh登录 db2和db2-priv [rootdb1 ~]# ssh db1 ^C [rootdb1 ~]# ssh db2 Last login: Wed May 14 18:25:19 2025 from db2 [rootdb2 ~]# ssh db2 Last login: Wed May 14 18:25:35 2025 from db1 [rootdb2…...

《AI大模型应知应会100篇》第64篇:构建你的第一个大模型 Chatbot

第64篇&#xff1a;构建你的第一个大模型 Chatbot 手把手教你从零开始搭建一个基于大模型的聊天机器人 摘要 你是否想过&#xff0c;自己也能构建一个像 ChatGPT 一样能对话、能思考的聊天机器人&#xff08;Chatbot&#xff09;&#xff1f;别担心&#xff0c;这并不需要你是…...

鸿蒙OSUniApp 实现精美的用户登录和注册页面#三方框架 #Uniapp

UniApp 实现精美的用户登录和注册页面 前言 在开发鸿蒙APP时&#xff0c;登录注册页面作为用户与应用交互的第一道门槛&#xff0c;其体验与质量往往决定了用户的第一印象。去年我接手了一个电商小程序项目&#xff0c;产品经理特别强调要做一个既美观又实用的登录注册页面。…...

c#中equal方法与gethashcode方法之间有何关联?

文章目录 前言一、对hash运算的深入思考二、equal与gethashcode的关联三、 equal与gethashcode不同步的后果四、 规范的重写gethashcode 前言 大家有没有遇到过&#xff0c;当你重写了c#对象的equal方法之后&#xff0c;编译器会提示你对相应的gethashcode进行重写&#xff0c…...

查询公网IP地址的方法:查看自己是不是公网ip,附内网穿透外网域名访问方案

本地搭建服务并提供互联网连接时&#xff0c;较为传统的方法是使用公网IP地址。因此&#xff0c;如何查询本地自己是不是公网IP&#xff0c;是必须要掌握的一种技巧。当面对确实无公网IP时&#xff0c;则可以通过内网穿透方案&#xff0c;如nat123网络映射工具&#xff0c;将本…...

【轻松学 C:编程小白的大冒险】— 16 函数的定义与调用

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【轻松学 C&#xff1a;编程小白的大冒险…...

【Tools】CPU 分析

CPU 分析 Windows SDK 本指南提供了可用于调查影响评估指标的中央处理单元 (CPU) 相关问题的详细技术。 特定于评估的分析指南中的各个指标或问题部分确定了需要调查的常见问题。 本指南提供了可用于调查这些问题的技术和工具。 本指南中的技术使用 Windows Performance Too…...

进阶2_1:QT5多线程与定时器共生死

1、在widget.ui中使用 LCD Number控件 注意&#xff1a;若 LCD 控件不是多线程&#xff0c;LCD控件则会瞬间自增到最大的数值&#xff0c;如上图&#xff0c;说明两者都是多线程处理 2、实现方式 1、创建 LCD 控件并修改为 LCD1 2、创建任务类 mytask. h&#xff0c;对任务类…...

ECharts:数据可视化的强大引擎

在当今这个信息爆炸的时代&#xff0c;如何有效地展示和理解复杂的数据成为了每一个开发者和技术爱好者面临的挑战。Apache ECharts 作为一款基于 JavaScript 的开源可视化库&#xff0c;以其强大的功能、丰富的图表类型以及高度的可定制性&#xff0c;迅速成为了数据可视化领域…...

记录 QT 在liunx 下 QFileDialog 类调用问题 ()Linux下QFileDialog没反应)

1. 2025.05.14 踩坑记录 因为QT 在 liunx 文件系统不同导致的 Windows &#xff1a; QString filePath QFileDialog::getOpenFileName(nullptr, "选择文件", ".", "文本文件 (*.txt);所有文件 (*.*)"); 没问题 liunx 下 打不开&#xff…...

通用软件项目技术报告 - 导读III

现在,我们正式进入报告的第六个主要领域:6. 领域六:与第三方服务/API 集成 (含 LLM API)。 连接: 在现代软件开发中,很少有应用程序是完全孤立的。我们经常需要与各种外部的第三方服务或 API 进行集成,以利用它们提供的特定功能(如支付处理、地图服务、社交媒体登录、云…...

Kali Linux 桌面环境安装与配置指南

一、为什么选择 Kali Linux&#xff1f; Kali Linux 由 Offensive Security 维护&#xff0c;集成了数百种安全测试工具&#xff08;如 Metasploit、Nmap 和 Burp Suite&#xff09;&#xff0c;非常适合安全研究。但需要注意的是&#xff0c;Kali 默认以 root 用户运行&#…...