移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——14.哈希(2)(模拟实现)

1.概念介绍

1.1开散列

开散列(Open Hashing),也叫链地址法,是一种解决哈希冲突的方法。每个哈希表槽位保存一个链表,所有散列到同一位置的元素都存储在该链表中。当插入元素发生冲突时,将新元素添加到相应槽位的链表末尾。

1.2开散列 

闭散列(Closed Hashing),也叫开放地址法,是一种解决哈希冲突的方法。当插入元素发生冲突时,通过寻找下一个空槽位来存储冲突元素,常见策略包括1.线性探测、2.二次探测等,不使用链表存储冲突元素。

2.模拟实现 

2.1闭散列模拟实现

1.枚举——status

enum status  //使用枚举保存数据的“状态”,如果为EMPTY,DELETE则可以插入
{EMPTY,//记得EMPTY要写在最上面,这样,系统默认的构造函数才会将s初始化为EMPTYEXIST,DELETE
};

2.数组中的元素——hashdata

template<class K,class V>
struct hashdata
{pair<K, V> data;     //数据status s;    //状态
};

3.将key元素转化为可计算的形式——hashfunc

为了确定元素应当插入到哪个位置,需要把key取出来

template<class K>
struct hashfunc     //因为key不一定是整形,如果能强制转换成整形,那就要转换
{size_t operator()(const K& key){return size_t(key);          }
};

单独为string类写一份:

template<>
struct hashfunc<string>     //单独为k=string写一份,还记得嘛,这是模板的特化!!!!!!!这样,在初始化hashtable时就不用再传仿函数模板参数了
{size_t operator()(const string& key){size_t flag = 0;;for (auto e : key){flag *= 31;//这一步可以保证abc,acb的flag不同,防止第一步就发生冲突flag += e;//本质是遍历整个string并使每个字母的ascii码相加,当然也可以使用其他的方式}return flag;}
};

模板特化相关知识:移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——9.模板进阶-CSDN博客

4.容器——hashtable

私有成员:

 private:vector<hashdata<K,V>> table;size_t num=0;//储存的关键字的个数

2.2.hashtable的功能实现

1.初始化

hashtable()
{table.resize(10);
}

2.插入!!!!!!

bool insert(const pair<K, V>& kv)
{if (find(kv.first)){return false;}//负载因子,指关键字个数在总size中的占比,(越大代表发生hash冲突的概率越大)普遍超出0.7时就要扩容了,扩容需要重新开一份空间!!!!!!!!!因为映射关系被打乱了if (num * 10 / table.size() == 7)//这里很巧妙{size_t newsize = table.size() * 2;hashtable<K, V,hash> newtable;newtable.table.resize(newsize);//遍历旧表for (size_t i = 0; i < table.size();i++){if (table[i].s == EXIST){newtable.insert(table[i].data);}}table.swap(newtable.table);//记得交换一下}hash hf;//1.线性探测(解决hash冲突的方法)size_t position = hf(kv.first) % table.size();//应用映射公式 hash(key) = key %  capacity (注意!!!!!这里要用table.size(),而不是table.capacity(),所以要除余while ((table[position]).s == EXIST)//如果当前的位置非空,则往后顺延一位!!!!!!{position++;position %= table.size();//positin走到底后回到0}table[position].data = kv;table[position].s= EXIST;++num;return true;
}

3.查找

hashdata<K, V>* find(const K& key)//查找是从确定的初始位置查找到nullptr!!!!!结束,因为没到nullptr前,都有可能是因为冲突导致数据后移
{hash hf;size_t position =hf(key)% table.size();while (table[position].s != EMPTY){if (table[position].data.first == key&& table[position].s==EXIST){return &table[position];}position++;position %= table.size();}return NULL;
}

4.删除

bool erase(const K& key)
{hashdata<K, V>* ret = find(key);if (ret){ret->s = DELETE;--num;return true;}else{return false;}
}

2.3开散列模拟实现

开散列存储的本质是指针数组

1.数组中的元素——hashnode

template<class T> 
struct hashnode
{T data;     //数据hashnode* next;hashnode(T kv):data(kv),next(nullptr){}};

2. 容器——hashtable

私有成员:

private:vector<node*> table;size_t num = 0;};

2.4.hashtable内容实现

1.初始化

hashtable()
{table.resize(10);
}

2.析构函数

~hashtable()
{for (size_t i = 0; i < table.size(); i++){node* cur = table[i];while (cur){node* next = cur->next;delete cur;cur = next;}table[i] = nullptr;//最后置空}}

3.查找

node* find(const K& key)
{hash hf;typeoft tt;size_t position = hf(key) % table.size();node* cur = table[position];while (cur){if (tt(cur->data)== key)return cur;cur = cur->next;}return nullptr;
}

4.插入

bool insert(const T& kv)
{hash hf;typeoft tt;if (find(tt(kv)))return true;if (num == table.size())//当负载因子等于1时要扩容{vector<node*> newtable;newtable.resize(table.size()* 2, nullptr);//遍历旧表for (size_t i = 0; i < table.size(); i++){node* cur = table[i];while (cur){node* next = cur->next;size_t newposition = hf(tt(cur->data)) % newtable.size();cur->next = newtable[newposition];newtable[newposition] = cur;cur = next;}table[i] = nullptr;//数据原来的位置处一定要置空,否则会因为二次析构产生问题}table.swap(newtable);//直接交换两个哈希桶(底层指针的交换)}size_t position = hf(tt(kv)) % table.size();node* newnode = new node(kv);node* cur = table[position];//头插newnode->next = cur;table[position] = newnode;num++;}

 3. 迭代器的设置(以开散列为例)!!!!!!!!

1.hsiterator的设置与功能

//前置声明,因为哈希表用到了迭代器,迭代器也用到了哈希表,这叫做相互依赖,需要做前置声明
template<class K, class T, class typeoft, class hash >
class hashtable;template<class K, class T,class ref,class ptr, class typeoft,class hash=hashfunc<K>>
struct hsiterator
{typedef hashnode<T> node;const hashtable< K, T, typeoft, hash> &point;//这里使用引用是为了防止析构影响原来的tabletypedef hsiterator<K, T,ref,ptr, typeoft,hash> Self;node* _node;size_t place;hsiterator(node* node_, const hashtable< K, T, typeoft, hash> &_point,size_t _place):_node(node_),point(_point),place(_place){}Self operator++(){if (_node->next)//如果—_node->next不为空,那么桶里面还有数据,走next{_node = _node->next;}else     //如果为空,那么需要走到下一个桶{typeoft tt;hash hf;//size_t head = hf(tt(_node->data)) % point.table.size();//找到初始位置,方便转移至下一个桶++place;while (place < point.table.size()){if (point.table[place]){_node = point.table[place];break;}else{place++;}}if (place == point.table.size()){_node = nullptr;}return *this;}}ref operator*(){return _node->data;}ptr operator->(){return &_node->data;}bool operator!=(const Self& s){return _node != s._node;}
};

2.hashtable中对hsiterator的封装 

template<class K, class T, class ref, class ptr,class typeoft,class hash>
friend struct hsiterator;//这里设置了友元,这样,hsiterator就可以直接取到hashtable的private成员table数组了typedef hsiterator<K, T,T&,T*, typeoft, hash> iterator;//普通迭代器
typedef hsiterator<K, T, const T&, const T*, typeoft, hash> const_iterator;//const迭代器iterator begin()
{for (size_t i = 0; i < table.size(); i++){if (table[i])return iterator(table[i], *this, i);}return end();
}iterator end()
{return iterator(nullptr, *this, -1);//-1是随便给的
}

4.unorderedmap&&unorderedset封装

1.取出K元素(仿函数)

struct setkeyoft //仿函数
{const K& operator()(const K& key){return key;}};

2.迭代器封装

set的iterator全部使用const迭代器:

 typedef typename hashtable<K,K,setkeyoft>::const_iterator iterator;typedef typename hashtable<K,K,setkeyoft>::const_iterator const_iterator;
/* iterator begin(){return table.begin();}iterator end(){return table.end();}*/const_iterator begin()const{return table.begin();}const_iterator end() const{return table.end();}

map的迭代器正常分类使用:

typedef typename hashtable<K, pair<const K, V>, setkeyoft>::iterator iterator;
typedef typename hashtable<K, pair<const K, V>, setkeyoft>::const_iterator const_iterator;iterator begin()
{return table.begin();
}iterator end()
{return table.end();
}const_iterator begin() const
{return table.begin();
}const_iterator end()  const
{return table.end();
}

5.代码全览

1.hash.h

#include<iostream>
#include<vector>
using namespace std;namespace close_address//闭散列
{enum status  //使用枚举保存数据的“状态”,如果为EMPTY,DELETE则可以插入{EMPTY,//记得EMPTY要写在最上面,这样,系统默认的构造函数才会将s初始化为EMPTYEXIST,DELETE};template<class K,class V>struct hashdata{pair<K, V> data;     //数据status s;    //状态};template<class K>struct hashfunc     //因为key不一定是整形,如果能强制转换成整形,那就要转换{size_t operator()(const K& key){return size_t(key);          }};template<>struct hashfunc<string>     //单独为k=string写一份,还记得嘛,这是模板的特化!!!!!!!这样,在初始化hashtable时就不用再传仿函数模板参数了{size_t operator()(const string& key){size_t flag = 0;;for (auto e : key){flag *= 31;//这一步可以保证abc,acb的flag不同,防止第一步就发生冲突flag += e;//本质是遍历整个string并使每个字母的ascii码相加,当然也可以使用其他的方式}return flag;}};//struct hashfuncstring     //这是单独为string类写的转换仿函数//{//	size_t operator()(const string& key)//	{//		size_t flag = 0;;//		for (auto e : key)//		{//			flag *= 31;//这一步可以保证abc,acb的flag不同//			flag += e;//本质是遍历整个string并使每个字母的ascii码相加,当然也可以使用其他的方式//		}//		return flag;//	}//};template<class K,class V,class hash=hashfunc<K>>//这里hash直接给了缺省值,如果K可以转化,就可以在初始化的时候可以不给hash的模板参数class hashtable{public:hashtable(){table.resize(10);}hashdata<K, V>* find(const K& key)//查找是从确定的初始位置查找到nullptr!!!!!结束,因为没到nullptr前,都有可能是因为冲突导致数据后移{hash hf;size_t position =hf(key)% table.size();while (table[position].s != EMPTY){if (table[position].data.first == key&& table[position].s==EXIST){return &table[position];}position++;position %= table.size();}return NULL;}bool insert(const pair<K, V>& kv){if (find(kv.first)){return false;}//负载因子,指关键字个数在总size中的占比,(越大代表发生hash冲突的概率越大)普遍超出0.7时就要扩容了,扩容需要重新开一份空间!!!!!!!!!因为映射关系被打乱了if (num * 10 / table.size() == 7)//这里很巧妙{size_t newsize = table.size() * 2;hashtable<K, V,hash> newtable;newtable.table.resize(newsize);//遍历旧表for (size_t i = 0; i < table.size();i++){if (table[i].s == EXIST){newtable.insert(table[i].data);}}table.swap(newtable.table);//记得交换一下}hash hf;//1.线性探测(解决hash冲突的方法)size_t position = hf(kv.first) % table.size();//应用映射公式 hash(key) = key %  capacity (注意!!!!!这里要用table.size(),而不是table.capacity(),所以要除余while ((table[position]).s == EXIST)//如果当前的位置非空,则往后顺延一位!!!!!!{position++;position %= table.size();//positin走到底后回到0}table[position].data = kv;table[position].s= EXIST;++num;return true;}bool erase(const K& key){hashdata<K, V>* ret = find(key);if (ret){ret->s = DELETE;--num;return true;}else{return false;}}void print(){for (size_t i = 0; i < table.size(); i++){if (table[i].s == EXIST){//printf("[%d]->%d\n", i, table[i].data.first);cout << "[" << i << "]->" << table[i].data.first << endl;}else if (table[i].s == EMPTY){//printf("[%d]->空余\n", i);cout << "[" << i << "]->空余" << endl;}else{//printf("[%d]->删除\n", i);cout << "[" << i << "]->删除" << endl;}}}private:vector<hashdata<K,V>> table;size_t num=0;//储存的关键字的个数};void test1(){hashtable<int, int> it;int a[] = { 4,14,24,34,5,7,1 };for (auto e : a){it.insert(make_pair(e, e));}it.insert(make_pair(3, 3));it.insert(make_pair(3, 3));it.insert(make_pair(-3, -3));it.print();cout << endl;it.erase(3);it.print();}void test2(){hashtable<string, int> it;string arr[] = { "香蕉","苹果" ,"西瓜" ,"苹果" ,"香蕉" ,"香瓜" ,"苹果" ,"香蕉" };for (auto e : arr){auto f = it.find(e);//hashdata<K,V>*if (f){f->data.second++;}else{it.insert(make_pair(e, 1));}}it.print();}
}namespace open_address//开散列
{template<class K>struct hashfunc     //因为key不一定是整形,如果能强制转换成整形,那就要转换{size_t operator()(const K& key){return size_t(key);}};template<>struct hashfunc<string>     //还记得嘛,这是模板的特化!!!!!!!这样,在初始化hashtable时就不用再传仿函数模板参数了{size_t operator()(const string& key){size_t flag = 0;;for (auto e : key){flag *= 31;//这一步可以保证abc,acb的flag不同,防止第一步就发生冲突flag += e;//本质是遍历整个string并使每个字母的ascii码相加,当然也可以使用其他的方式}return flag;}};template<class T>struct hashnode{T data;     //数据hashnode* next;hashnode(T kv):data(kv),next(nullptr){}};//前置声明,因为哈希表用到了迭代器,迭代器也用到了哈希表,这叫做相互依赖,需要做前置声明template<class K, class T, class typeoft, class hash >class hashtable;template<class K, class T,class ref,class ptr, class typeoft,class hash=hashfunc<K>>struct hsiterator{typedef hashnode<T> node;const hashtable< K, T, typeoft, hash> &point;//这里使用引用是为了防止析构影响原来的tabletypedef hsiterator<K, T,ref,ptr, typeoft,hash> Self;node* _node;size_t place;hsiterator(node* node_, const hashtable< K, T, typeoft, hash> &_point,size_t _place):_node(node_),point(_point),place(_place){}Self operator++(){if (_node->next)//如果—_node->next不为空,那么桶里面还有数据,走next{_node = _node->next;}else     //如果为空,那么需要走到下一个桶{typeoft tt;hash hf;//size_t head = hf(tt(_node->data)) % point.table.size();//找到初始位置,方便转移至下一个桶++place;while (place < point.table.size()){if (point.table[place]){_node = point.table[place];break;}else{place++;}}if (place == point.table.size()){_node = nullptr;}return *this;}}ref operator*(){return _node->data;}ptr operator->(){return &_node->data;}bool operator!=(const Self& s){return _node != s._node;}};template<class K, class T, class typeoft ,class hash = hashfunc<K>>class hashtable{public:typedef hashnode<T> node;template<class K, class T, class ref, class ptr,class typeoft,class hash>friend struct hsiterator;//这里设置了友元,这样,hsiterator就可以直接取到hashtable的private成员table数组了typedef hsiterator<K, T,T&,T*, typeoft, hash> iterator;//普通迭代器typedef hsiterator<K, T, const T&, const T*, typeoft, hash> const_iterator;//const迭代器iterator begin(){for (size_t i = 0; i < table.size(); i++){if (table[i])return iterator(table[i], *this, i);}return end();}iterator end(){return iterator(nullptr, *this, -1);//-1是随便给的}const_iterator begin()const{for (size_t i = 0; i < table.size(); i++){if (table[i])return const_iterator(table[i], *this, i);}return end();}const_iterator end() const{return const_iterator(nullptr, *this, -1);//-1是随便给的}hashtable(){table.resize(10);}~hashtable(){for (size_t i = 0; i < table.size(); i++){node* cur = table[i];while (cur){node* next = cur->next;delete cur;cur = next;}table[i] = nullptr;//最后置空}}node* find(const K& key){hash hf;typeoft tt;size_t position = hf(key) % table.size();node* cur = table[position];while (cur){if (tt(cur->data)== key)return cur;cur = cur->next;}return nullptr;}bool insert(const T& kv){hash hf;typeoft tt;if (find(tt(kv)))return true;if (num == table.size())//当负载因子等于1时要扩容{vector<node*> newtable;newtable.resize(table.size()* 2, nullptr);//遍历旧表for (size_t i = 0; i < table.size(); i++){node* cur = table[i];while (cur){node* next = cur->next;size_t newposition = hf(tt(cur->data)) % newtable.size();cur->next = newtable[newposition];newtable[newposition] = cur;cur = next;}table[i] = nullptr;//数据原来的位置处一定要置空,否则会因为二次析构产生问题}table.swap(newtable);//直接交换两个哈希桶(底层指针的交换)}size_t position = hf(tt(kv)) % table.size();node* newnode = new node(kv);node* cur = table[position];//头插newnode->next = cur;table[position] = newnode;num++;}bool erase(const K& key){hash hf;typeoft tt;size_t position = hf(key) % table.size();node* cur = table[position];node* prev = nullptr;while (cur){if (tt(cur->data) == key){if (prev){prev->next = cur->next;delete cur;num--;}else{table[position] = nullptr;num--;}return true;}prev = cur;cur = cur->next;}return false;}private:vector<node*> table;size_t num = 0;};}

2.myunorderedmap.h

#include"hash.h"using namespace open_address;namespace zone
{template<class K, class V>class unorderedmap{public:struct setkeyoft{const K& operator()(const pair<const K, V>& key){return key.first;}};typedef typename hashtable<K, pair<const K, V>, setkeyoft>::iterator iterator;typedef typename hashtable<K, pair<const K, V>, setkeyoft>::const_iterator const_iterator;iterator begin(){return table.begin();}iterator end(){return table.end();}const_iterator begin() const{return table.begin();}const_iterator end()  const{return table.end();}bool insert(const pair<K,V>& key){return table.insert(key); }private:hashtable<K, pair<const K,V>, setkeyoft> table;};void testmap(){unorderedmap<string, string> it;it.insert(make_pair("sort","排序"));it.insert(make_pair("right","右"));it.insert(make_pair("left","左"));it.insert(make_pair("middle","中"));for (auto e : it){e.second += 'x';//map的value可改变,但key不能改变cout << e.first<<' '<<e.second<<endl;//记得加一个.first,因为重载的operator*,只会取得data,在map中就是pair<k,v>,所以要用.first取得key}}}

3.myunorderedset.h

#include"hash.h"using namespace open_address;namespace zone
{template<class K>class unorderedset{public:struct setkeyoft //仿函数{const K& operator()(const K& key){return key;}};typedef typename hashtable<K,K,setkeyoft>::const_iterator iterator;typedef typename hashtable<K,K,setkeyoft>::const_iterator const_iterator;/* iterator begin(){return table.begin();}iterator end(){return table.end();}*/const_iterator begin()const{return table.begin();}const_iterator end() const{return table.end();}bool insert(const K& key){return table.insert(key);}private:hashtable<K,K, setkeyoft> table;};void testset(){unorderedset<int> it;it.insert(2);it.insert(3);it.insert(14);it.insert(24);it.insert(34);unorderedset<int>::iterator arr = it.begin();while (arr != it.end()){//*arr += 5;//set的key不可修改cout << *arr << endl;++arr;}}}

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

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

相关文章

Ansible概述

目录 一、ansible简介 二、absible的特点 三、ansible的工作原理以及流程 四、ansible环境安装部署 五、ansible命令行模块 六、inventory 主机清单 一、ansible简介 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。…

HCIP-HarmonyOS Application Developer 习题(十二)

&#xff08;多选&#xff09;1、声明式开发范式的转场动画包含以下哪几种类型? A、页面间转场 B、应用间转场 C、共享元素转场 D、组件内转场 答案&#xff1a;ACD 分析&#xff1a; &#xff08;多选&#xff09;2、公共事件服务为应用程序提供哪些能力。 A、取消发布公共…

基于STM32设计的养殖场环境监测系统(华为云IOT)

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成【4】需求总结 1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】设备端开发【2】上位机开发 二、部署华为云物联网平台2.1 物联网平台介绍2.2 开通物联网服务2.3 创建产品&#x…

开源项目 - 轻量级人体姿态 人体关键点检测 机器视觉 深度学习

开源项目 - 轻量级人体姿态 人体关键点检测 机器视觉 深度学习 项目地址&#xff1a;https://gitcode.net/EricLee/light_pose 1、数据集来源&#xff1a;coco2017 数据集 * coco 数据集官方网站&#xff1a;https://cocodataset.org/#home * [数据集下载地址(百度网盘 Pa…

力扣2653.滑动窗口的美丽值

给你一个长度为 n 的整数数组 nums &#xff0c;请你求出每个长度为 k 的子数组的 美丽值 。 一个子数组的 美丽值 定义为&#xff1a;如果子数组中第 x 小整数 是 负数 &#xff0c;那么美丽值为第 x 小的数&#xff0c;否则美丽值为 0 。 请你返回一个包含 n - k 1 个整数…

Java项目-基于springcloud框架的分布式架构网上商城系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

​​【项目建设PPT模板】中台建设,中台设计,数字中台整体建设方案(PPT)

工业互联网数字中台解决方案旨在为企业提供全面、高效的数据驱动能力。该方案主要包括以下几个核心部分&#xff1a; 数据中台&#xff1a;作为核心&#xff0c;数据中台负责汇聚、整合、提纯和加工各类工业数据&#xff0c;实现数据资产的标准化、模型化和模块化。通过提供API…

【Pycharm】显示内存不足the IDE is running low on memory解决方法

Pycharm提示显示内存不足the IDE is running low on memory解决方法 在右上角找到Help&#xff0c;点击&#xff0c;找到change memory settings 修改数值如1024&#xff0c;2048 等&#xff0c;增大容量即可。最后点击save and Restart

红日安全vulnstack (一)

目录 环境搭建 本机双网卡 Kali IP 靶机IP Web GetShell 前期信息收集 Yxcms后台模板 Getshell PHPMyAdmin日志 Getshell into outfile写入一句话 X phpmyadmin 日志写入一句话 后渗透 MSF 生成木马上线 提取用户hash值 **hash**加密方式 MSF权限Shell至CS CS …

如何使用FastAPI开发Serverless应用?

使用FastAPI开发Serverless应用是一种现代且高效的方法&#xff0c;它结合了FastAPI的高性能和Serverless架构的灵活性、可扩展性以及低成本。下面是一个基本指南&#xff0c;帮助你从零开始创建并部署一个FastAPI应用到Serverless环境。 1. 安装FastAPI和Uvicorn 首首先&…

基于springboot美食推荐商城的设计与实现

基于springboot美食推荐商城的设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;idea 源码获取&#xff1a;https:…

YOLO11 目标检测 | 导出ONNX模型 | ONNX模型推理

本文分享YOLO11中&#xff0c;从xxx.pt权重文件转为.onnx文件&#xff0c;然后使用.onnx文件&#xff0c;进行目标检测任务的模型推理。 用ONNX模型推理&#xff0c;便于算法到开发板或芯片的部署。 备注&#xff1a;本文是使用Python&#xff0c;编写ONNX模型推理代码的 目…

【Vercel】Vercel静态部署踩坑

背景 在现代的软件开发中&#xff0c;自动化部署是一个不可或缺的环节。Vercel作为一个流行的前端部署平台&#xff0c;提供了与GitHub的无缝集成&#xff0c;使得开发者能够在每次提交代码后自动触发部署流程。然而&#xff0c;自动化部署过程中可能会遇到一些挑战&#xff0…

全网免费的文献调研方法以及获取外网最新论文、代码和翻译pdf论文的方法(适用于硕士、博士、科研)

1. 文献调研 学术搜索引擎(十分推荐前三个&#xff0c;超有用)&#xff1a;使用 Google Scholar(https://scholar.google.com/)(https://scholar.google.com.tw/)(巨人学术搜索‬‬)、&#xff08;三个都可以&#xff0c;镜像网站&#xff09; arXiv(https://arxiv.org/)、&am…

qt页面设计

1. Designer 设计师&#xff08;掌握&#xff09; Designer是Qt内置的一款界面设计程序&#xff0c;设计的界面文件为.ui格式。 C程序员通常不会单独启动Designer&#xff0c;如果要在项目中使用Designer程序&#xff0c;只需要在新建项目时&#xff0c;勾选“创建界面文件”选…

学习C语言(25)

整理今天的学习内容 预处理详解 1.预定义符号 C语言设置了一些预定义符号&#xff0c;可以直接使用&#xff0c;预定义符号是在预处理期间处理的 __FILE__&#xff08;进行编译的源文件&#xff09; __LINE__ &#xff08;文件当前的行号&#xff09; __DATE__&#xff08…

Visual Studio 2022安OpenCV可视化工具image watch

1. 打开 VS2022 &#xff0c;扩展 -管理扩展 2. 搜索 Image Watch 关闭VS2022 后 安装 打开视图、调出 Image Watch 窗口 测试代码&#xff1a; #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.…

分布式系统中的Dapper与Twitter Zipkin:链路追踪技术的实现与应用

目录 一、什么是链路追踪&#xff1f; 二、核心思想Dapper &#xff08;一&#xff09;Dapper链路追踪基本概念概要 &#xff08;二&#xff09;Trace、Span、Annotations Trace Span Annotation 案例说明 &#xff08;三&#xff09;带内数据与带外数据 带外数据 带…

【Linux】进程间通信---匿名管道、命名管道(超详解)

目录 匿名管道 管道的创建&#xff1a; 创建子进程&#xff1a; 关闭不需要的fd: 发送消息&#xff1a; 管道的5种特征&#xff1a; 管道的4种情况&#xff1a; 命名管道 创建命名管道&#xff1a; 删除命名管道&#xff1a; 手写命名管道&#xff1a; 完整代码&am…

多线程(七):单例模式指令重排序

目录 1. 单例模式 1.1 饿汉模式 1.2 懒汉模式 2. 懒汉模式下的问题 2.1 线程安全问题 2.2 如何解决 --- 加锁 2.3 加锁引入的新问题 --- 性能问题 2.4 指令重排序问题 2.4.1 指令重排序 2.4.2 指令重排序引发的问题 1. 单例模式 单例模式, 是设计模式中最典型的一种模…