【数据结构】深入理解哈希及其底层数据结构

目录

一、unordered系列关联式容器

二、底层结构

2.1 哈希的概念

 2.2 哈希冲突(哈希碰撞)

2.3 哈希函数

2.4 哈希冲突处理

2.4.1 闭散列(开放定址法)

2.4.1.1 代码实现:

2.4.2 开散列(链地址法,较优)

2.4.2.1 扩容

 2.4.2.2 仿函数实现多类型储存

2.4.2.3 代码实现 

 2.4.3 开散列与闭散列比较

 三、哈希表的模拟实现(加迭代器)

1.unordered_set

 2.unordered_map.h

3.test.c


一、unordered系列关联式容器

        在C++11中一共添加了4个unordered系列关联式容器,它们提供了基于哈希表的实现,以平均常数时间复杂度进行元素的查找、插入和删除操作。  分别为

std::unordered_map       std::unordered_multimap
std::unordered_set       std::unordered_multiset

        这些unordered系列的容器与STL中的mapmultimapsetmultiset相对应,但后者是基于红黑树实现的,提供的是有序的元素集合,而前者的实现则基于哈希表,提供的是无序的元素集合,且在平均情况下,对元素的查找、插入和删除操作具有更好的性能。

二、底层结构

unordered系列的容器之所以效率高,归功于它的底层哈希结构。 

2.1 哈希的概念

        在顺序结构或者树中,元素的储存位置与其值没有什么对应关系,一般查找一个值时我们都需要去经过多次比较,时间复杂度为O(n),平衡树中为树的高度,效率与比较次数直接挂钩。

这时我们想有没有一个理想的方法可以不经过任何比较,一次直接从表中得到要搜索的元素呢?

        如果能够构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立 一一映射的关系,那么在查找时通过该函数可以很快找到该元素

 在插入时:根据待插入元素的关键码,用哈希函数计算出该元素的存储位置并存放

 在查找时:同插入一样,对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

        大佬们将这种方法称为哈希(或者散列)方法,而哈希方法中计算存储位置的函数称为哈希函数,构造出的表叫做哈希表(散列表)。

例如:

 2.2 哈希冲突(哈希碰撞)

 在插入时有时多个值通过哈希函数的计算会计算处相同的哈希地址,该种现象称为哈希冲突或哈希碰撞

2.3 哈希函数

哈希冲突是无法避免的,但只要我们设计出合理的哈希函数,就能极大的降低哈希冲突的概率 

哈希函数的设计有几个原则:

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值 域必须在0到m-1之间 
  • 哈希函数计算出来的地址能均匀分布在整个空间中
  • 哈希函数应该比较简单

常见的哈希函数:   

1. 直接定址法(常用) :

取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B

优点:简单、均匀

缺点:需要事先知道关键字的分布情况,适合查找比较小且连续的情况

 2.除留余数法(常用):

设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数, 按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址

常用的哈希函数还有数字分析法、平方取中法、折叠法、随机数法等,但上述两种方法最为常用。

哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突

2.4 哈希冲突处理

解决哈希冲突两种常见的方法是:闭散列和开散列

2.4.1 闭散列(开放定址法)

 闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有 空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。

这里我们主要讲解线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止

插入: 通过哈希函数获取待插入元素在哈希表中的位置

           如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突, 使用线性             探测找到下一个空位置,插入新元素

删除:采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会              影响其他元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此            线性探测采用标记的伪删除法来删除一个元素

2.4.1.1 代码实现:
template<class K>
class HashFunc
{
public:size_t operator()(const K& key){return (size_t)key;}
};template<>
class HashFunc<string>
{
public:size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;hash *= 131;}return hash;}
};namespace Open_address
{//枚举状态enum State{EMPTY,EXIST,DELETE};template<class K,class V>struct HashData{pair<K, V> _kv;//值State _state = EMPTY;//状态标记};template<class K,class V,class Hash=HashFunc<K>>class HashTable{public:HashTable(size_t size = 10){_tables.resize(size);}HashData<K, V>* Find(const K& key){Hash hs;// 线性探测size_t hashi = hs(key) % _tables.size();while (_tables[hashi]._state != EMPTY){if (key == _tables[hashi]._kv.first&& _tables[hashi]._state == EXIST){return &_tables[hashi];}++hashi;hashi %= _tables.size();}return nullptr;}bool Insert(const pair<K, V>& kv){if (Find(kv.first))return false;if (_n * 10 / _tables.size() >= 7){HashTable<K, V, Hash> newHT(_tables.size() * 2);for (auto& e : _tables){if (e._state == EXIST){newHT.Insert(e._kv);}}_tables.swap(newHT._tables);}Hash hs;size_t hashi = hs(kv.first) % _tables.size();while (_tables[hashi]._state==EXIST){hashi++;hashi %= _tables.size();}_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;++_n;return true;}bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret){_n--;ret->_state = DELETE;return true;}else{return false;}}private:vector<HashData<K, V>> _tables;size_t _n = 0;};
}

负载因子:表内元素/表的长度 

 对于开放寻址法来说,由于所有元素都存储在哈希表的数组中,并且不使用额外的数据结构(如链表)来处理冲突,因此负载因子的控制尤为重要。一旦负载因子过高,就可能导致哈希表性能急剧下降,因为插入和查找操作可能需要遍历更多的槽位才能找到所需元素或空槽位。

一般控制在0.7~0.8之间 

闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷

2.4.2 开散列(链地址法,较优)

 开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链 接起来,各链表的头结点存储在哈希表中。

开散列中每个桶中放的都是发生哈希冲突的元素

2.4.2.1 扩容

桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,可 能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对哈希表进行增容,那该条件怎么确认呢?开散列最好的情况是:每个哈希桶中刚好挂一个节点, 再继续插入元素时,每一次都会发生哈希冲突,因此,在元素个数刚好等于桶的个数时,可以给哈希表增容

//负载因子到1就扩容
if (_n == _tables.size())
{vector<Node*> newTables(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); i++){// 取出旧表中节点,重新计算挂到新表桶中Node* cur = _tables[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = hs(cur->_kv.first) % newTables.size();cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);
}
 2.4.2.2 仿函数实现多类型储存

如果想要存储各种类型的数据,我们可以通过传仿函数来实现

template<class K>
class HashFunc
{
public:size_t operator()(const K& key){return (size_t)key;}
};//特化
template<>
class HashFunc<string>
{
public:size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;hash *= 131;}return hash;}
};template<class K,class V,class Hash=HashFunc<K>>
class Hashtable;
2.4.2.3 代码实现 

代码实现:

template<class K>
class HashFunc
{
public:size_t operator()(const K& key){return (size_t)key;}
};template<>
class HashFunc<string>
{
public:size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;hash *= 131;}return hash;}
};namespace hash_bucket
{template<class K,class V>struct HashNode{HashNode<K, V>* _next;pair<K, V> _kv;HashNode(const pair<K, V>& kv):_next(nullptr), _kv(kv){}};template<class K,class V,class Hash=HashFunc<K>>class Hashtable{typedef HashNode<K, V> Node;public:Hashtable(){_tables.resize(10, nullptr);_n = 10;}~Hashtable(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}Node* Find(const K& key){Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (cur->_kv.first == key){return cur;}cur = cur->_next;}return nullptr;}bool Insert(const pair<K, V>& kv){if (Find(kv.first))return false;Hash hs;//负载因子到1就扩容if (_n == _tables.size()){vector<Node*> newTables(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); i++){// 取出旧表中节点,重新计算挂到新表桶中Node* cur = _tables[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = hs(cur->_kv.first) % newTables.size();cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hs(kv.first) % _tables.size();Node* newNode = new Node(kv);//头插newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return true;}bool Erase(const K& key){Hash hs;size_t hashi = hs(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (cur->_kv.first == key){// 删除if (prev){prev->_next = cur->_next;}else{_tables[hashi] = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n;};}

 2.4.3 开散列与闭散列比较

        应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。

        事实上, 由于开地址法必须保持大量的空闲空间以确保搜索效率,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。

 三、哈希表的模拟实现(加迭代器)

1.unordered_set

unordered_set.h

#pragma once
#include"Hashtable.h"namespace L
{template<class K, class Hash = HashFunc<K>>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::Hashtable<K, const K, SetKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}bool insert(const K& key){return _ht.Insert(key);}bool find(const K& key){return _ht.Find(key);}bool erase(const K& key){return _ht.Erase(key);}private:hash_bucket::Hashtable<K, const K, SetKeyOfT, Hash> _ht;};}

Hashtable.h 

#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include<string>template<class K>
class HashFunc
{
public:size_t operator()(const K& key){return (size_t)key;}
};template<>
class HashFunc<string>
{
public:size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;hash *= 131;}return hash;}
};namespace hash_bucket
{template<class T>struct HashNode{HashNode<T>* _next;T _data;HashNode(const T& data):_next(nullptr), _data(data){}};//前置声明template<class K, class T, class KeyOfT, class Hash>class Hashtable;template<class K,class T,class KeyOfT,class Hash>struct __HTIterator{typedef HashNode<T> Node;typedef Hashtable<K, T, KeyOfT, Hash> HT;typedef __HTIterator<K, T, KeyOfT, Hash> Self;Node* _node;HT* _ht;__HTIterator(Node* node,HT* ht):_node(node),_ht(ht){}T& operator*(){return _node->_data;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) %_ht->_tables.size();hashi++;while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}hashi++;}if (hashi == _ht->_tables.size()){_node = nullptr;}}return *this;}bool operator!=(const Self& s){return _node != s._node;}};template<class K, class T, class KeyOfT,class Hash>class Hashtable{typedef HashNode<T> Node;//友元 template<class K, class T, class KeyOfT, class Hash>friend struct __HTIterator;public:typedef __HTIterator<K, T, KeyOfT, Hash> iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]){return iterator(_tables[i], this);}}return end();}iterator end(){return iterator(nullptr, this);}Hashtable(){_tables.resize(10, nullptr);_n = 10;}~Hashtable(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}Node* Find(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return cur;}cur = cur->_next;}return nullptr;}bool Insert(const T& data){KeyOfT kot;if(Find(kot(data)))return false;Hash hs;//负载因子到1就扩容if (_n == _tables.size()){vector<Node*> newTables(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); i++){// 取出旧表中节点,重新计算挂到新表桶中Node* cur = _tables[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = hs(kot(cur->_data)) % newTables.size();cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hs(kot(data)) % _tables.size();Node* newNode = new Node(data);//头插newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return true;}bool Erase(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (cur->kot(cur->_data) == key){// 删除if (prev){prev->_next = cur->_next;}else{_tables[hashi] = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n;};
}

 2.unordered_map.h

#pragma once
#include"Hashtable.h"
namespace L
{template<class K, class V, class Hash = HashFunc<K>>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::Hashtable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}bool insert(const pair<K, V>& kv){return _ht.Insert(kv);}private:hash_bucket::Hashtable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};}

Hashtable.h

#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include<string>template<class K>
class HashFunc
{
public:size_t operator()(const K& key){return (size_t)key;}
};template<>
class HashFunc<string>
{
public:size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;hash *= 131;}return hash;}
};namespace hash_bucket
{template<class T>struct HashNode{HashNode<T>* _next;T _data;HashNode(const T& data):_next(nullptr), _data(data){}};//前置声明template<class K, class T, class KeyOfT, class Hash>class Hashtable;template<class K,class T,class KeyOfT,class Hash>struct __HTIterator{typedef HashNode<T> Node;typedef Hashtable<K, T, KeyOfT, Hash> HT;typedef __HTIterator<K, T, KeyOfT, Hash> Self;Node* _node;HT* _ht;__HTIterator(Node* node,HT* ht):_node(node),_ht(ht){}T& operator*(){return _node->_data;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) %_ht->_tables.size();hashi++;while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}hashi++;}if (hashi == _ht->_tables.size()){_node = nullptr;}}return *this;}bool operator!=(const Self& s){return _node != s._node;}};template<class K, class T, class KeyOfT,class Hash>class Hashtable{typedef HashNode<T> Node;//友元 template<class K, class T, class KeyOfT, class Hash>friend struct __HTIterator;public:typedef __HTIterator<K, T, KeyOfT, Hash> iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]){return iterator(_tables[i], this);}}return end();}iterator end(){return iterator(nullptr, this);}Hashtable(){_tables.resize(10, nullptr);_n = 10;}~Hashtable(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}Node* Find(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return cur;}cur = cur->_next;}return nullptr;}bool Insert(const T& data){KeyOfT kot;if(Find(kot(data)))return false;Hash hs;//负载因子到1就扩容if (_n == _tables.size()){vector<Node*> newTables(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); i++){// 取出旧表中节点,重新计算挂到新表桶中Node* cur = _tables[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = hs(kot(cur->_data)) % newTables.size();cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hs(kot(data)) % _tables.size();Node* newNode = new Node(data);//头插newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return true;}bool Erase(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (cur->kot(cur->_data) == key){// 删除if (prev){prev->_next = cur->_next;}else{_tables[hashi] = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n;};
}

3.test.c

#include"My_unordered_map.h"
#include"My_unordered_set.h"
//void test_set1()
//{
//	L::unordered_set<int> us;
//	us.insert(3);
//	us.insert(1);
//	us.insert(5);
//	us.insert(15);
//	us.insert(45);
//	us.insert(7);
//
//	L::unordered_set<int>::iterator it = us.begin();
//	while (it != us.end())
//	{
//		//*it += 100;
//		cout << *it << " ";
//		++it;
//	}
//	cout << endl;
//
//	for (auto e : us)
//	{
//		cout << e << " ";
//	}
//	cout << endl;
//}
//
//void test_map1()
//{
//	L::unordered_map<string, string> dict;
//	dict.insert(make_pair("sort", "1"));
//	dict.insert(make_pair("left", "2"));
//	dict.insert(make_pair("right", "3"));
//
//
//	L::unordered_map<string, string>::iterator it = dict.begin();
//	while (it != dict.end())
//	{
//		cout << (*it).first << " " << (*it).second << endl;
//		//cout << *it.first << " " << *it.second << endl;
//		/*pair<string, string> t=*it;
//		cout << t.first << " " << t.second<<endl;*/
//		++it;
//	}
//	cout << endl;
//	//for (auto& kv : dict)
//	//{
//	//	//kv.first += 'x';
//	//	kv.second += 'y';
//
//	//	cout << kv.first << ":" << kv.second << endl;
//	//}
//}int main()
{
//	L::test_set1();
//	L::test_map1();return 0;
}

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

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

相关文章

Visual Studio 2022 安装及使用

一、下载及安装 VS 官网&#xff1a;Visual Studio: IDE and Code Editor for Software Developers and Teams 下载免费的社区版 得到一个.exe文件 右键安装 选择C开发&#xff0c;并修改安装位置 等待安装 点击启动 二、VS的使用 1.创建项目 打开VS&#xff0c;点击创建新项…

java设计模式(十六)职责链模式(Chain of Responsibility Pattern)

1、模式介绍&#xff1a; 职责链模式是一种行为设计模式&#xff0c;其中多个对象按顺序处理请求&#xff0c;直到其中一个对象能够处理请求为止。请求沿着链传递&#xff0c;直到有一个对象处理它为止。 2、应用场景&#xff1a; 职责链模式适用于以下场景&#xff1a;请求…

初学51单片机之UART串口通信

CSDN其他博主的博文&#xff08;自用&#xff09;嵌入式学习笔记9-51单片机UART串口通信_51uart串口通讯-CSDN博客 CSDN其他博主的博文写的蛮好&#xff0c;如果你想了解51单片机UART串口可以点进去看看&#xff1a; UART全称Universal Asynchronous Receiver/Transmitter即通…

常用知识碎片 分页组件的使用(arco-design组件库)

目录 分页组件使用 API 组件代码示例 使用思路&#xff1a; 前端示例代码 html script 后端示例代码 Controller Impl xml 总结 分页组件使用 使用Arco Design之前需要配置好搭建前端环境可以看我另外一篇文章&#xff1a; 手把手教你 创建Vue项目并引入Arco Desi…

生存人数00

题目链接 生存人数 题目描述 注意点 假设所有人都出生于 1900 年至 2000 年&#xff08;含 1900 和 2000 &#xff09;之间birth[i] < death[i]如果有多个年份生存人数相同且均为最大值&#xff0c;输出其中最小的年份 解答思路 初始想到的是遍历birth&#xff0c;更新…

B2B领域的客户裂变策略:打造行业内的共赢生态

在日益竞争激烈的B2B市场中&#xff0c;客户裂变作为一种高效的增长策略&#xff0c;不仅能够帮助企业快速扩大客户基础&#xff0c;还能促进行业内资源共享与合作&#xff0c;共同构建一个健康、可持续的共赢生态。本文将探讨B2B领域实施客户裂变策略的关键要素&#xff0c;以…

无障碍全免费上手智能体:Autogen Studio结合Deepseek Coder打造一款AI旅游规划师

本文的唯一目的是通过打造一款AI旅游规划师&#xff0c;通俗易懂、深入浅出的讲清楚AI应用的大方向-智能体-的原理。 无需科学上网&#xff0c;无需付费API&#xff0c;无需编程能力&#xff0c;一小时即可部署、搭建一款复杂的、多代理交互的AI智能体-旅游规划师&#xff0c;…

【深度学习】PyTorch深度学习笔记02-线性模型

1. 监督学习 2. 数据集的划分 3. 平均平方误差MSE 4. 线性模型Linear Model - y x * w 用穷举法确定线性模型的参数 import numpy as np import matplotlib.pyplot as pltx_data [1.0, 2.0, 3.0] y_data [2.0, 4.0, 6.0]def forward(x):return x * wdef loss(x, y):y_pred…

java入门1.5.0

前言&#xff1a; 在java入门1.4.0中&#xff0c;我们快速构建了一个基于Maven管理的Spring boot3项目&#xff0c;对基本文件结构有了初步的认知&#xff0c;创建了git仓库 正片: 看山是山&#xff0c;看山不是山&#xff0c;看山还是山&#xff0c;下面两段代码很好了验证这…

vue3项目中浏览器打开本地文档或者下载本地应用的方法(2024-07-11)

在public文件夹下面加入预览的文件【操作说明文档】。 此文件夹不会压缩并且路径不变&#xff0c;所以是最佳的存放文件的位置。 代码&#xff1a; <template><n-icon title"操作文档" style"cursor: pointer;margin-right: 10px;" size"2…

MATLAB中使用HDL Coder生成HDL代码时的报错整理

Delay balancing unsuccessful because an extra 4 cycles of latency introduced by optimizations in the feedback loop cannot be offset using design delays for the loop latency budget. 产生原因 由于时序考虑&#xff0c;在每个模块的输出端添加了1到2级的输入输出流…

深圳比创达|EMC与EMI测试整改:打造电磁“绿色”产品的必经之路4

深圳比创达&#xff5c;EMC与EMI测试整改&#xff1a;打造电磁“绿色”产品的必经之路 随着电子技术的飞速发展&#xff0c;电子设备在日常生活和工业生产中的应用越来越广泛。然而&#xff0c;这些设备在运行时产生的电磁辐射&#xff08;EMI&#xff09;和对外界电磁干扰的敏…

java基础复习

初识 JDK&#xff08;开发包&#xff09; JRK&#xff08;运行环境&#xff09; 解释型语言 数据类型&#xff1a; 基本数据类型&#xff1a;byte short int(默认) long float double(默认) char boolean 引用数据类型&#xff1a;类 接口 数组 扩展&#xff1a; BigDecimal…

从人工巡检到智能预警:视频AI智能监控技术在水库/河湖/水利防汛抗洪中的应用

一、背景需求分析 近日&#xff0c;我国多省市遭遇连日暴雨&#xff0c;导致水库、湖泊、河道等水域水位暴涨&#xff0c;城市内涝频发。随着夏季汛期的到来&#xff0c;降雨天气频繁&#xff0c;水利安全管理面临严峻挑战。为保障水库安全、预防和减少洪涝灾害&#xff0c;采…

MVC分页

public ActionResult Index(int ? page){IPagedList<EF.ACCOUNT> userPagedList;using (EF.eMISENT content new EF.eMISENT()){第几页int pageNumber page ?? 1;每页数据条数&#xff0c;这个可以放在配置文件中int pageSize 10;//var infoslist.C660List.OrderBy(…

7.11 cf div3 A

Problem - A - Codeforces 题目概述 求最少问题数量&#xff0c;以确保能举办m轮比赛&#xff0c;每轮包含一个难度级别为 ‘A’ 到 ‘G’ 的问题。 ac代码 #include<bits/stdc.h> typedef long long ll;#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) …

最新 Kubernetes 集群部署 + flannel 网络插件(保姆级教程,最新 K8S 1.28.2 版本)

资源列表 操作系统配置主机名IP所需插件CentOS 7.92C4Gk8s-master192.168.60.143flannel-cni-plugin、flannel、coredns、etcd、kube-apiserver、kube-controller-manager、kube-proxy、 kube-scheduler 、containerd、pause 、crictlCentOS 7.92C4Gk8s-node01192.168.60.144f…

Nodejs 第八十六章(部署pm2)

Node.js如何部署? 如果要部署Nodejs项目&#xff0c;第一点肯定是需要有台服务器&#xff0c;第二点需要一个部署工具这里使用pm2 PM2 PM2 是一个非常流行的 Node.js 进程管理工具&#xff0c;用于在生产环境中运行和监控 Node.js 应用程序。它提供了多种功能&#xff0c;帮…

自建邮局服务器相比云邮箱有哪些优势特性?

自建邮局服务器如何配置&#xff1f;搭建自建邮局服务器的技术&#xff1f; 尽管云邮箱服务提供了便捷和低成本的解决方案&#xff0c;自建邮局服务器依然具有许多独特的优势和特性&#xff0c;吸引了众多企业和组织。AokSend将深入探讨自建邮局服务器相比云邮箱的主要优势。 …

BigMarket-基础层持久化数据库

需求 工程对接数据库 图例 结构说明 app-主要用于启动&#xff0c;没有业务逻辑 domain-业务逻辑&#xff0c;如积分的兑换&#xff0c;抽奖&#xff0c; infrastructure-基础层&#xff0c;技术支持&#xff0c;数据服务数据持久化&#xff1a;MySQL&#xff0c;redis&am…