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

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

1.概念介绍

1.1开散列

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

ba807042c8964dd09d0c795f8be5ac37.png

1.2闭散列

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

bd45a379160c427bbe211a88b230c64c.png

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;}}}

 

 

相关文章:

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

1.概念介绍 1.1开散列 开散列&#xff08;Open Hashing&#xff09;&#xff0c;也叫链地址法&#xff0c;是一种解决哈希冲突的方法。每个哈希表槽位保存一个链表&#xff0c;所有散列到同一位置的元素都存储在该链表中。当插入元素发生冲突时&#xff0c;将新元素添加到相应…...

请描述一下JVM(Java虚拟机)的生命周期及其对应用程序性能的影响

1、请描述一下JVM&#xff08;Java虚拟机&#xff09;的生命周期及其对应用程序性能的影响。 JVM&#xff08;Java虚拟机&#xff09;的生命周期主要涉及以下几个阶段&#xff1a;加载、验证、准备、解析、执行、卸载。每个阶段都有其特定的作用和影响。 加载&#xff1a;JVM…...

展会邀约|加速科技与您相约IC China 2024!

第二十一届中国国际半导体博览会&#xff08; IC China 2024&#xff09;将于 2024 年11月18日—11月20日在北京国家会议中心举行。加速科技将携高性能测试机ST2500EX、ST2500E、eATE及全系测试解决方案亮相E2馆B150展位。博览会期间&#xff0c;将同期举办"半导体产业前沿…...

鸿蒙中服务卡片数据的获取和渲染

1. 2.在卡片中使用LocalStorageProp接受传递的数据 LocalStorageProp("configNewsHead") configNewsHeadLocal: ConfigNewsHeadInfoItem[] [] 注意&#xff1a;LocalStorageProp括号中的为第一步图片2中的键 3.第一次在服务卡片的第一个卡片中可能会获取不到数据…...

运维篇-修复centos7无法下载docker问题

修复centos7无法下载docker问题 1、安装docker时报错2、docker无法下载镜像 1、安装docker时报错 linux的centos系统&#xff0c;安装docker时会报错 –> Finished Dependency Resolution Error: Package: glibc-2.17-307.el7.1.i686 (base) Requires: glibc-common 2.17…...

【论文阅读】WaDec: Decompiling WebAssembly Using Large Language Model

论文阅读笔记:WaDec: Decompiling WebAssembly Using Large Language Model 1. 来源出处 论文标题: WaDec: Decompiling WebAssembly Using Large Language Model作者: Xinyu She, Yanjie Zhao, Haoyu Wang会议: 39th IEEE/ACM International Conference on Automated Softwar…...

redis类型介绍

1. 字符串&#xff08;String&#xff09;&#xff1a; • 简介&#xff1a;最基础的数据类型&#xff0c;可以存储任何形式的字符串&#xff0c;包括文本数据和数字数据。 • 常用操作&#xff1a;SET、GET、INCR、DECR等。 2. 列表&#xff08;List&#xff09;&#xff1a; …...

kubernetes如何配置默认存储

如果不想每次都创建PV&#xff0c;希望k8s集群中能够配置号默认存储&#xff0c;然后根据你的PVC自动创建PV&#xff0c;就需要安装一个默认存储&#xff0c;也就是storageclass 什么是storageclass Kubernetes提供了一套可以自动创建PV的机制&#xff0c;即&#xff1a;Dyna…...

【微服务】Spring AI 使用详解

目录 一、前言 二、Spring AI 概述 2.1 什么是Spring AI 2.2 Spring AI 特点 2.3 Spring AI 带来的便利 2.4 Spring AI 应用领域 2.4.1 聊天模型 2.4.2 文本到图像模型 2.4.3 音频转文本 2.4.4 嵌入大模型使用 2.4.5 矢量数据库支持 2.4.6 数据工程ETL框架 三、Sp…...

DataGrip 连接 dm

参考链接 使用DataGrip链接达梦数据库_datagrip连接达梦数据库-CSDN博客 下载 jdbc 驱动包 第一种 通过链接下载&#xff1a;下载 第二种【特指 window 安装包】 在达梦安装包 iso 文件里面 source/drivers/jdbc 将驱动添加进 DataGrip 选中 jdbc 驱动包&#xff0c;然后选…...

数据库监控工具DBdoctor v3.2.4.3版本发布,新增对openGauss、Vastbase G100的支持!

新引擎扩展 新增对openGauss数据库的支持&#xff1a;支持对openGauss数据库的SQL审核、实例巡检、性能洞察、锁透视、根因诊断、基础监控、索引推荐、存储分析&#xff1b; 新增对Vastbase G100数据库的支持&#xff1a;支持对Vastbase G100数据库的SQL审核、实例巡检、性能洞…...

Git 常用命令大全与详解

Git 是一种广泛使用的分布式版本控制系统。无论是管理个人项目还是进行团队协作&#xff0c;掌握 Git 的常用命令都是开发者必备的技能之一。本文将介绍一些常用的 Git 命令&#xff0c;并对其进行详细说明。 1. 基础命令 初始化仓库 git init&#xff1a;在当前目录下初始化…...

执行flink sql连接clickhouse库

手把手教学&#xff0c;flink connector打通clickhouse大数据库&#xff0c;通过下发flink sql&#xff0c;来使用ck。 组件版本jdk1.8flink1.17.2clickhouse23.12.2.59 1.背景 flink官方不支持clickhouse连接器&#xff0c;工作中难免会用到。 2.方案 利用GitHub大佬提供…...

什么是C++中的友元函数和友元类?

友元函数&#xff08;Friend Function&#xff09;和 友元类&#xff08;Friend Class&#xff09;是用于控制类的访问权限的机制。这允许特定的函数或类访问另一个类的私有成员和保护成员&#xff0c;打破了 C 的封装性规则。 友元函数 定义 友元提供了不同类的成员函数之间…...

基于Spring Boot+Vue的多媒体素材管理系统的设计与实现

一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17 前端&#xff1a; 技术&#xff1a;框架Vue.js&#xff1b;UI库&#xff1a;ElementUI&#xff1b; 开发工具&…...

Inpaint-Web:纯浏览器端实现的开源图像处理工具

之前在刷短视频的时候&#xff0c;经常看到一些情侣在景区拍照&#xff0c;结果被路人“抢镜”。有时男朋友会拿出手机&#xff0c;帮忙把那些路人“P”掉&#xff0c;简直是既贴心又有趣。最近我在逛 GitHub 时&#xff0c;发现了一个可以在浏览器端删除照片中部分内容的纯前端…...

商业物联网详细指南:优势与挑战

物联网是信息技术行业最具前景的领域之一。为什么它如此热门呢&#xff1f;原因在于全球连接性。设备可以像人群一样相互协作。正如我们所知&#xff0c;协作能显著提高生产力。 物联网对普通用户和企业都有益处。许多日常流程可以通过传感器、扫描仪、摄像头和其他设备实现自…...

如何在项目中用elementui实现分页器功能

1.在结构部分复制官网代码&#xff1a; <template> 标签: 这是 Vue 模板的根标签&#xff0c;包含所有的 HTML 元素和 Vue 组件。 <div> 标签: 这是一个普通的 HTML 元素&#xff0c;包裹了 el-pagination 组件。它没有特别的意义&#xff0c;只是为了确保 el-pagi…...

Nginx参数配置-笔记

文章目录 upstream实现后台应用服务负载均衡&高可用proxy_set_header参数 upstream实现后台应用服务负载均衡&高可用 角色IPnginx172.168.110.2后端应用服务1172.168.110.3后端应用服务2172.168.110.4后端应用服务3(备用)172.168.110.5 示例如下&#xff1a; upstre…...

衡量神经网络表征相似度

目录 1.中心核对齐技术(CKA)2.Hilbert-Schmidt independence criterion(HSIC)HSIC的计算步骤:HSIC的性质:应用:矩阵中心化操作对于单个数据集的中心化对于两个数据集的中心化(例如,用于HSIC)Python代码示例1.中心核对齐技术(CKA) CKA通过计算两个表征的Gram矩阵(即…...

Javascript高级:深度解析与多种实现方式数组扁平化

数组扁平化&#xff1a;深度解析与多种实现方式 在JavaScript编程中&#xff0c;数组扁平化是一个常见的操作&#xff0c;指的是将一个多维数组转换成一个一维数组。这个过程中&#xff0c;所有嵌套的数组元素都会被“拉平”到同一个层级。数组扁平化在处理嵌套数据结构时非常…...

SpringBoot Data Redis连接Redis-Cluster集群

使用SpringBoot Data Redis无法连接Redis-Cluster集群 最近在研究系统高并发下的缓存架构&#xff0c;因此自己在自己买的云服务器上搭建好Redis 5.0 版本的集群后&#xff0c;使用springboot的 RedisTemplate连接是发现总是访问不到集群节点。上网百度了发现没有好的解决办法&…...

计算机网络——TCP篇

TCP篇 基本认知 TCP和UDP的区别? TCP 和 UDP 可以使用同一个端口吗&#xff1f; 可以的 传输层中 TCP 和 UDP在内核中是两个完全独立的软件模块。可以根据协议字段来选择不同的模块来处理。 TCP 连接建立 TCP 三次握手过程是怎样的&#xff1f; 一次握手:客户端发送带有 …...

【网络安全面经】技术性问题3

11. 一次完整的 HTTP 请求过程 域名解析&#xff1a;通过 DNS 将域名转换为 IP 地址&#xff0c;如上述 DNS 的工作原理。建立 TCP 连接&#xff1a;客户端向服务器发送 SYN 报文段&#xff0c;经过三次握手建立 TCP 连接。发送 HTTP 请求&#xff1a;客户端向服务器发送 HTTP…...

前后端交互之动态列

一. 情景 在做项目时&#xff0c;有时候后会遇到后端使用了聚合函数&#xff0c;导致生成的对象的属性数量或数量不固定&#xff0c;因此无法建立一个与之对应的对象来向前端传递数据&#xff0c;这时可以采用NameDataListVO向前端传递数据。 Data Builder AllArgsConstructo…...

递归(3)----力扣40组合数2,力扣473火柴拼正方形

给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 示例 1: 输入: candidates [10,1,2,7,6,1…...

十一:HTTP 状态码详解:解读每一个响应背后的意义

HTTP(超文本传输协议)是网络通信的基石之一,主要用于客户端(例如浏览器)和服务器之间的通信。为了让服务器能准确地向客户端反馈请求的处理状态,HTTP设计了一套标准的状态码。每一个状态码代表了特定的含义,指示了请求的状态、潜在的问题或成功的信息。 1. 信息响应 (1…...

《译文》2024年11月数维杯国际大学生数学建模挑战赛题目

# 赛题正式发布 2024年第十届数维杯国际大学生数学建模挑战赛顺利开赛&#xff0c;竞赛开始时间为北京时间2024年11月15日09:00至北京时间2024年11月19日09:00&#xff0c;共计4天&#xff0c;竞赛题目正式发布&#xff0c;快来一起围观&#xff0c;你认为今年的哪个题目更具有…...

shell命令统计文件行数之和

你可以使用以下 shell 命令来统计每个 .txt 文件的行数,并将其加和在一起: find . -name "*.txt" -not -name "*.json" -exec wc -l {} + | awk {sum += $1} END {print sum} 解释: find . -name "*.txt" -not -name "*.json": f…...

第02章 CentOS基本操作

2.文件基本操作【文件操作&#xff08;一&#xff09;】 目标 理解Linux下路径的表示方法能够使用命令(mkdir和touch)在指定位置创建目录和文件能够使用命令(rm)删除指定的目录和文件能够使用命令(ls)列出目录里的文件能够使用命令(cat,head,tail,less,more)查看文件内容理解标…...

网站建设的作用有哪些/小红书推广引流软件

软件下载地址: 趣盘下载 软件功能&#xff1a; 专门针对Discuz! 7论坛开发的看贴工具。快速读取指定栏目下的贴子&#xff08;按指定页数读取&#xff09;&#xff0c;是一种贴子快速浏览工具。同时&#xff0c;本软件也可以下载指定的贴子&#xff0c;你完全可以把他们做为贴…...

18款未成年软件入口/河南网站seo推广

面试场景 面试官&#xff1a;Java多线程了解吗&#xff1f;你给我写一下&#xff0c;起两个线程交替打印0~100的奇偶数。 小黄&#xff1a;啊&#xff1f; 面试官&#xff1a;就是有两个线程&#xff0c;一个线程打印奇数另一个打印偶数&#xff0c;它们交替输出&#xff0c…...

小贷网站需要多少钱可以做/技成培训网

Hive 有很多的基础数据类型&#xff0c;在实际构建数仓的这么多数据类型&#xff0c;我们该如何选择呢&#xff1f; 如果你的公司足够大&#xff0c;那么建议参照以下几条建议&#xff1a; 浮点数建议&#xff1a; 1) Hive 中Double类型需要慎用, Hive 中Double类型存在失真。 …...

wordpress主题汉化版免费下载/徐州seo推广优化

MongoDB管理工具 作者&#xff1a;nosqlfan on 星期四, 五月 20, 2010 5条评论 【阅读&#xff1a;8,012 次】 看一个产品是否得到认可,可以从一个侧面看其第三方工具的数量和成熟程度,下面我们就来细数一下MongoDB的各个GUI管理工具. Try MongoDB 不用安装就可以试用MongoDB…...

wordpress有中文版没/百度客户管理系统登录

Xamarin的学习门槛挺高的&#xff0c;体现在两方面。 第一方面&#xff1a;国内使用人数少&#xff0c;中文资料更是少之又少&#xff0c;不像原声的开发&#xff0c;遇到问题一搜一大堆&#xff0c;Xamarin开发遇到问题只能“股沟”一下。对于英文不好或者没有“股沟”习惯的…...

wordpress b2b源码/电商运营基础知识

IntelliJ IDEA是Java语言开发的集成环境&#xff0c;IntelliJ在业界被公认为优秀的Java开发工具之一&#xff0c;尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查、 创新的GUI设计等方面的功能可以说是超常的。 点击下载IntelliJ IDEA最新试…...