c++学习之哈希

目录

1.关于unordered系列关联式容器

2.关于unordered_map

 3.哈希(散列)表的实现

一,直接定址法

二,除留余数法

方法一:闭散列:开放定址法 

方法二:闭散列:哈希桶/拉链法

 4.哈希表的封装

哈希表封装后

unordered_map简单封装

unordered_set简单封装


1.关于unordered系列关联式容器

      在unordered系列关联式容器是C++11中新增的一组关联式容器,它们的 底层结构是哈希表 而不是红黑树(我可以理解之前的map和set红黑树就是Tree_map Tree_set,这里的就是hash_map,hash_set) 。这些容器包括 unordered_map、unordered_set unordered_multimap unordered_multiset 它们的使用方式与红黑树结构的关联式容器基本相同,只是它们的底层结构不同。
相对于红黑树的Tree结构,hash这里更加强调的是特性,即unorder(无序性)。

 unordered_map和unordered_set是最常用的两个容器,它们的底层结构都是哈希,unordered_map是存储<key, value>键值对的关联式容器,它允许通过key快速的索引到与其对应的value。  

  在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。在内部,unordered_map没有对<key, value>按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低。

   unordered_set是一个存储唯一元素的集合,它的底层结构也是哈希表。它的元素是不可重复的,因此它的查询速度非常快。

2.关于unordered_map

先来看看库中的介绍:

对于map,还是以key_value为数据模型的,那么这里的key肯定也是不可以修改的,unodered_map强调的是查找效率,其次对于其迭代器是单向的。

对于它的接口也大差不差,,但是有两个我们没见过的,实际上在实现unordered_map中引入了其他参数及接口,如这里的负载因子(load_factor),哈希桶(Buckts).

 3.哈希(散列)表的实现

  什么是哈希呢?
     通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立 一一映射的关系,那么在查找时通过该函数可以很快找到该元素。
当向该结构中:
插入元素--
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放。
搜索元素--
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置
取元素比较,若关键码相等,则搜索成功。
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称
为哈希表(Hash Table)(或者称散列表)。
比如在做oj第一个出现的字符时,就可以通过字符的ASCLL与下标建立一一映射的关系。
那么对于这种结构,我们首先最重要的是就是去建立一一的映射关系 (关键值->存储位置)
线面说说两种定值方法:

一,直接定址法

所谓的直接定址法,即直接用该值(可以给这个值加或减或者不变)作为关键值,让每一个值都有一个唯一位置,让他的存储位置与该值建立关系。(用存储位置,表示key值)
但是当值太过于分散时,那么在映射时,就需要开辟足够大的空间,去存储。

 数据集中的时候我们开辟空间就小,但太分散时,如2作为key值插入位置2,95作为key插入位置95,9999插入位置9999.那对于空间无疑是巨大的浪费,那该怎么办呢?

二,除留余数法

有人就提出太大,那就对这个数取模,让它处在在一个合适的位置,比如上面7个数据,size为7,那就对每个数据模7,使它在这里个范围之内。

但是会出现新的问题,可能有数据会冲突,她两模完值是一样的,但对应的value是不一样的,这种问题被叫做哈希碰撞/哈希冲突,那么如何解决哈希碰撞呢?这时候有两种法案可以解决:

方法一:闭散列:开放定址法 

本质上就是如果新插入的数据的位置已经有数据了(value不一样),那就在这个开放的空间中,重新找一个空位置。这里也会有两种查找新位置的方法--1.线性探测(一个个往后找)2.二次探测(以2次方递增查找)。

如果冲突,我们就往后找没有被占的位置。可是当我们的后面空间快要满员时,此时再往后找新的位置,就可能位置存在不够的情况,那么在实际上,插入数据的个数在空间达到70%等的时候就需要扩容了,这里我么就引入了一个参数-负载因子(存储关键字的个数/空间大小)来空间空间大小。

为什么会有两种探测或者其他方式来寻找都是避免多次冲突,比如对于线性探测,重新找位置的话,如果有一部分数据是连续的,一个位置被提前占了,那么就会引起一片的哈希冲突,面对这个问题因此有了的探测二次探测。

那么我们就先来实现一下哈希表:

对于这里的负载因子,即不能太大,也不能太小,太大,容易发生冲突,太小,空间浪费太多。

这里我们一般使用0.7较为合适。

namespace myspace
{using namespace std;//如何去插入首先就需要对插入的每个位置做标记,该位置下可能存在三种状态,已经有数据,为空,之前有现在删除了enum State{EXIST,EMPTY,DELETE};//存放哈希表的数据的类型template<class K,class V>struct HashData{pair<K, V>  _kv;State _sta=EMPTY;};//类型转换,计算出key的大小,来确定平衡因子template<class K>struct HahFunc{size_t operator()(const K&k){return size_t(k);}};template<>struct HahFunc<string>{//如果是字符串,我们用所有字符的ascll的和表示key//但是字符串之和也是有很大可能重合,很多人通过在数学方面的研究出了一些解决办法//这里最好的方式是采用的BKDR方法,给每个字符乘以31,131....这样的数 之后的和大概率不会重复size_t operator()(const string& k){size_t sum = 0;for (int i = 0; i < k.size(); i++){sum = sum * 31;sum=sum+k[i];}return sum;}};
template<class K, class V,class Hash=HahFunc<V>>class HashTable
{
public:HashTable(){//初始化空间为tables.resize(10);}bool insert( const pair<K,V>& kv){if (find(kv.first) != NULL){return false;}//负载因子决定是否扩容if (_n * 10 / tables.size() == 7){//扩容//注意这里的扩容不能直接扩容,因为扩容之后size发生改变,对应的位置发生改变,因此需要重新开辟空间//在一个个重新(映射)插入,之后释放旧空间size_t newsize = tables.size() * 2;//扩二倍HashTable<K, V> newHaTa;newHaTa.tables.resize(newsize);//新表for (int i = 0; i < tables.size(); i++){newHaTa.insert(tables[i]._kv);//重新走一遍映射再插入其中}//现在的需要的哈希表是新的,交换过来tables.swap(newHaTa.tables);}//通过取模size使得对应的位置在该size内Hash hf;size_t hashi = hf(kv.first % tables.size()) ;while (tables[hashi]._sta == EXIST){//先确定好位置hashi++;hashi %= tables.size();}//再插入tables[hashi]._kv = kv;tables[hashi]._sta = EXIST;_n++;return true;}HashData<K,V>* find(const K &key){Hash hf;size_t hashi = hf(key % tables.size());while (tables[hashi]._sta!=EMPTY ){if (tables[hashi]._sta==EXIST&&tables[hashi]._kv.first == key){return &tables[hashi];}hashi++;hashi %= tables.size();}return NULL;}//伪删除bool erase(const K&key){HashData<K, V> tmp = find(key);if (tmp){tmp._sta == DELETE;_n--;return true;}else{return false;}}void Printf(){for (int i = 0; i < tables.size(); i++){if (tables[i]._sta == EXIST){cout<< i<<" " << tables[i]._sta << "->" << tables[i]._kv.first << endl;}else if (tables[i]._sta == DELETE){cout << i <<" " << "DELETE" << tables[i]._kv.first << endl;}else if (tables[i]._sta == EMPTY){cout << i <<" " << "EMPTY" << tables[i]._kv.first << endl;}}}private:vector<HashData<K,V>> tables;size_t _n;//插入的关键字的个数
};
}

方法二:闭散列:哈希桶/拉链法

不同于上述的除留余数法,在实际的应用当中,而是引用哈希桶的方法,所谓的哈希桶,就是将哈希冲突的值放一起内部处理,此时整体结构就是vecor<list>型的结构。

     每一个key对应有一个桶,相同也没事,放在一起内部解决。

我来们可以将上面的挂着的链表理解为桶,里面存放着相同key的值,但是当存放的值太多,遍历桶里的值时间复杂度就是O(N),效率太低,因此当长度达到某个界限时,就会换成红黑树来存放,提高查找效率。

在结构上,vector中的list,我们为了实现迭代器,我们自己写单链表,里面存放Node*,再插入时,我们采用头插的方式,如下图假设1,11,111他们的key值一样。

由于key类型不一定是整形,也有可能是其他类型,对于字符换类型,我们选他们的ascll码之和,再称31,用仿函数转化为size_t,以此来表示位置。

namespace Hash_Bucket
{using namespace std;template<class K>struct HahFunc{size_t operator()(const K& k){return size_t(k);}};template<>struct HahFunc<string>{//如果是字符串,我们用所有字符的ascll的和表示key//但是字符串之和也是有很大可能重合,很多人通过在数学方面的研究出了一些解决办法//这里最好的方式是采用的BKDR方法,给每个字符乘以31,131....这样的数 之后的和大概率不会重复size_t operator()(const string& k){size_t sum = 0;for (int i = 0; i < k.size(); i++){sum = sum * 31;sum = sum + k[i];}return sum;}};template<class K, class V > struct HashNode{pair<K, V> _kv;HashNode* next;HashNode(const pair<K, V>& kv):_kv (kv),next(nullptr){}};template<class K, class V,class Hash= HahFunc<K>>class HashTable{public:typedef HashNode<K, V> Node;HashTable(){_table.resize(10);}~HashTable(){//循环遍历释放桶的每一个节点for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->next;delete cur;cur = next;}_table[i] = nullptr;}}bool insert(const pair<K, V>& kv){Hash hf;if (find(kv.first)){//不插入相同的值return false;}//对于哈希桶,如果满了就要扩容,也就是负载因子为1if (_n == _table.size()){//第一种扩容方式,我们延续上面的扩容方式//size_t newsize = _table.size() * 2;//扩二倍//HashTable<K, V> newHaTa;//newHaTa.tables.resize(newsize);//新表//for (int i = 0; i < _table.size(); i++)//{//	Node* cur = _table[i];//	while (cur)//	{//newHaTa.insert(cur->kv);//重新走一遍映射再插入其中//	}//	//}现在的需要的哈希表是新的,交换过来//_table.swap(newHaTa.tables);//没必要用上述方式,我们直接重新弄个表,把节点挪动下来vector<Node*> newtable;newtable.resize(2 * _table.size());for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* Next = cur->next;//重新映射到新表当中size_t hashi = hf(kv.first) % newtable.size();//头插cur->next = newtable[i];//表中的新头newtable[i] = cur;cur = Next;//遍历下一个}//旧表置空_table[i] == nullptr;}//交换旧表与新表_table.swap(newtable);}//还是先通过取模节省空间size_t hashi = hf(kv.first) % _table.size();//头插Node* newnode = new Node(kv);newnode->next = _table[hashi];_table[hashi] = newnode;++_n;return true;}Node* find( const K&key){Hash hf;size_t hashi = hf( key) % _table.size();Node* cur = _table[hashi];while (cur){if (cur->_kv.first == key){return cur;}cur = cur->next;}return NULL;}bool erase(const K* key){Hash hf;size_t hashi = hf(key )% _table.size();Node* cur = _table[hashi];Node* prev = nullptr;while (cur){if (cur->_kv.first == key){//找到并删除//头删if (prev == nullptr){_table[hashi] = cur->next;//如果头被断开为空,cur的下一个就是新头节点}else{prev->next = cur->next;//和相对的头节点断开关系}delete cur;return true;}prev = cur;//上一个节点,相对下一节点的头节点cur = cur->next;//下一个节点}return false;}//那么实际上我们来看桶的大小其实并不会很大void Some(){size_t bucketSize = 0;size_t maxBucketLen = 0;size_t sum = 0;double averageBucketLen = 0;for (size_t i = 0; i < _table.size(); i++){Node* cur = _tables[i];if (cur){++bucketSize;}size_t bucketLen = 0;while (cur){++bucketLen;cur = cur->_next;}sum += bucketLen;if (bucketLen > maxBucketLen){maxBucketLen = bucketLen;}}averageBucketLen = (double)sum / (double)bucketSize;printf("all bucketSize:%d\n", _tables.size());printf("bucketSize:%d\n", bucketSize);printf("maxBucketLen:%d\n", maxBucketLen);printf("averageBucketLen:%lf\n\n", averageBucketLen);}void Print(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){if (cur){cout << cur->_kv.first << cur->_kv.second << endl;}		cur = cur->next;}}cout << endl;}private:vector<Node*> _table;//这里存放节点指针,目的是为了实现迭代器size_t _n;};

可能有些人觉得哈希桶可能太长,效率可能太低 ,但实际上哈希桶并不会太长,通过BKDR,以及负载因子的控制,不会有太多相同的key值。因此哈希桶实现的哈希表效率与上述基本不差。4.

 4.哈希表的封装

对于哈希表的封装,和封装红黑树一样,我们可以封装出unordered_map与unordered_map。

首先就是统一哈希表的模板参数,直接传pair,哈希表中用T表示,通过仿函数传T张key的类型,

 之后就是实现迭代器,迭代器与哈希表两者相互依赖,需要提前声明,以及再哈希表中声明友元迭代器方便我们使用,在之后为了实现const迭代器,在传入参数Ref,Ptr作为T&,T*.

对于迭代器的封装,这里我们需要三个参数,分别是哈希表(也可用vector),结点指针,以及下标位置hashi,通过遍历判断实现前置++。

哈希表封装后

namespace Hash_Bucket
{using namespace std;template<class K>struct HahFunc{size_t operator()(const K& k){return size_t(k);}};template<>struct HahFunc<string>{//如果是字符串,我们用所有字符的ascll的和表示key//但是字符串之和也是有很大可能重合,很多人通过在数学方面的研究出了一些解决办法//这里最好的方式是采用的BKDR方法,给每个字符乘以31,131....这样的数 之后的和大概率不会重复size_t operator()(const string& k){size_t sum = 0;for (int i = 0; i < k.size(); i++){sum = sum * 31;sum = sum + k[i];}return sum;}};template<class T > struct HashNode{T _data;HashNode* next;HashNode(const T& data):_data (data),next(nullptr){}};//迭代器//由于迭代器与哈希表存在双向依赖//我们在这里给上前置声明template<class K, class T, class keyofT, class Hash>class HashTable;template<class K, class T, class Ref, class Ptr, class keyofT, class Hash>struct HTiterator{typedef HashNode< T > Node;HTiterator(Node*node, HashTable< K, T, keyofT, Hash>* _tab,size_t _hashi):_node(node), tab(_tab),hashi(_hashi){}//这里除了传一个指针,还需要数组或整个表Node* _node;const HashTable< K, T, keyofT, Hash>* tab;size_t hashi;typedef HTiterator< K,  T, Ref,Ptr,keyofT,  Hash > self;//后置加加self &operator++(){if (_node->next){//继续走这个桶_node = _node->next;}else{//下一个桶,找下一个桶++hashi;//新的桶while (hashi < tab->_table.size()){if (tab->_table[hashi]){//如果不为空,我的节点就是这个新节点_node = tab->_table[hashi];break;}else {++hashi;}}if (hashi == tab->_table.size()){_node = nullptr;}}return *this;}Ptr operator->(){return &(_node->data);}Ref operator*(){return (_node->_data);}bool operator!=(const self &tmp){return _node != tmp._node;}};template<class K,class T,class keyofT,class Hash= HahFunc<K>>class HashTable{public://声明友元template<class K, class T,class Ref,class Ptr, class keyofT, class Hash >friend struct HTiterator;typedef HTiterator< K, T,T&,T*, keyofT, Hash > iterator;typedef HTiterator< K, T, const T&, const T*, keyofT, Hash > const_iterator;iterator begin(){for (int i = 0; i < _table.size(); i++){if (_table[i]){return iterator(_table[i],this,i);}}return end();}iterator end(){return iterator(nullptr, this,-1);}const_iterator begin()const{for (int 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);}typedef HashNode<T> Node;HashTable(){_table.resize(10);}~HashTable(){//循环遍历释放桶的每一个节点for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->next;delete cur;cur = next;}_table[i] = nullptr;}}Hash hf;keyofT kot;bool insert(const T& data){if (find(kot(data))){return false;}//对于哈希桶,如果满了就要扩容,也就是负载因子为1if (_n == _table.size()){vector<Node*> newtable;newtable.resize(2 * _table.size());for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* Next = cur->next;//重新映射到新表当中size_t hashi = hf(kot(data)) % newtable.size();//头插cur->next = newtable[i];//表中的新头newtable[i] = cur;cur = Next;//遍历下一个}//旧表置空_table[i] == nullptr;}//交换旧表与新表_table.swap(newtable);}//还是先通过取模节省空间size_t hashi = hf(kot(data)) % _table.size();//头插Node* newnode = new Node(data);newnode->next = _table[hashi];_table[hashi] = newnode;++_n;return true;}Node* find( const K&key){Hash hf;size_t hashi = hf( key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return cur;}cur = cur->next;}return NULL;}bool erase(const K* key){Hash hf;size_t hashi = hf(key )% _table.size();Node* cur = _table[hashi];Node* prev = nullptr;while (cur){if (kot(cur->data) == key){//找到并删除//头删if (prev == nullptr){_table[hashi] = cur->next;//如果头被断开为空,cur的下一个就是新头节点}else{prev->next = cur->next;//和相对的头节点断开关系}delete cur;return true;}prev = cur;//上一个节点,相对下一节点的头节点cur = cur->next;//下一个节点}return false;}//那么实际上我们来看桶的大小其实并不会很大void Some(){size_t bucketSize = 0;size_t maxBucketLen = 0;size_t sum = 0;double averageBucketLen = 0;for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){++bucketSize;}size_t bucketLen = 0;while (cur){++bucketLen;cur = cur->_next;}sum += bucketLen;if (bucketLen > maxBucketLen){maxBucketLen = bucketLen;}}averageBucketLen = (double)sum / (double)bucketSize;printf("all bucketSize:%d\n", _table.size());printf("bucketSize:%d\n", bucketSize);printf("maxBucketLen:%d\n", maxBucketLen);printf("averageBucketLen:%lf\n\n", averageBucketLen);}void Print(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){if (cur){cout << kot(cur->data) << kot(cur->data) << endl;}		cur = cur->next;}}cout << endl;}private:vector<Node*> _table;//这里存放节点指针,目的是为了实现迭代器size_t _n;};};

unordered_map简单封装

namespace myspace1
{using namespace std;template<class K, class V>class unordered_map{struct mapkeyofT{const K& operator()(const pair<K, V>& data){return data.first;}};public:typedef typename Hash_Bucket::HashTable< K, pair<K, V>, mapkeyofT >::iterator iterator;iterator begin(){return table.begin();}iterator end(){return table.end();}bool insert(const pair<K, V>& kv){return table.insert(kv);}private:Hash_Bucket::HashTable<K, pair<K, V>, mapkeyofT> table;};void test(){unordered_map<std::string, std::string> dictionary;dictionary.insert(std::make_pair<std::string, std::string>("苹果", "两个"));dictionary.insert(std::make_pair<std::string, std::string>("梨子", "两个"));dictionary.insert(std::make_pair<std::string, std::string>("香蕉", "两个"));unordered_map<std::string, std::string>::iterator it = dictionary.begin();while (it != dictionary.end()){cout << (*it).first<< (*it).second<<" ";++it;}}
};

unordered_set简单封装

namespace myspace2
{template<class K>class unodered_set{struct setkeyofT{const K& operator()(const K& key){return key;}};public:typedef typename Hash_Bucket::HashTable< K,K>, setkeyofT >::iterator iterator;iterator begin(){return table.begin();}iterator end(){return table.end();}bool insert(const K& key){return  table.insert(key);}private:Hash_Bucket::HashTable<K, K, setkeyofT> table;};
};

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

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

相关文章

机器学习/sklearn 笔记:K-means,kmeans++

1 K-means介绍 1.0 方法介绍 KMeans算法通过尝试将样本分成n个方差相等的组来聚类&#xff0c;该算法要求指定群集的数量。它适用于大量样本&#xff0c;并已在许多不同领域的广泛应用领域中使用。KMeans算法将一组样本分成不相交的簇&#xff0c;每个簇由簇中样本的平均值描…

hadoop shell操作 hdfs处理文件命令 hdfs上传命令 hadoop fs -put命令hadoop fs相关命令 hadoop(十三)

hadoop fs -help rm 查看rm命令作用 hadoop fs 查看命令 1. 创建文件夹&#xff1a; # hdfs前缀也是可以的。更推荐hadoop hadoop fs -mkdir /sanguo 2.上传至hdfs命令&#xff1a; 作用&#xff1a; 从本地上传hdfs系统 &#xff08;本地文件被剪切走&#xff0c;不存在了&…

模拟量采集----测量输入的电流

生活中的模拟量有很多 大多都为电压信号和电流信号 今天讲如何测量输入的电流信号 通过欧姆定律可知 电流测量的测量&#xff1a;是将电流加载在固定阻值的电阻上&#xff0c;来测量这个电阻二端的电压 最后反算出电流的大小 所用的公式是IU/R 我们使用仿真软件来看测量…

Visual Studio(VS) C++程序LNK2005错误,提示“error LNK2005: _XXX已经在xxx.obj中定义”解决方案

1.问题如图 2.出现原因 项目中有多个源文件或头文件&#xff0c;include后导致有些变量重复定义&#xff0c;加上Visual Studio新版版要求更严格 3.解决办法 查询到的解决办法很多不好用&#xff0c;此处记录解决自己问题的一个办法&#xff1a;直接让编译器忽略第二次定义的…

图形数据库的实战应用:如何在 Neo4j 中有效管理复杂关系

关系数据库管理系统( RDBMS ) 代表了最先进的技术&#xff0c;这在一定程度上要归功于其由周边技术、工具和广泛的专业技能组成的完善的生态系统。 在这个涵盖信息技术(IT) 和运营技术(OT) 的技术革命时代&#xff0c;人们普遍认识到性能方面出现了重大挑战&#xff0c;特别是…

初识JVM(简单易懂),解开JVM神秘的面纱

目录 一、什么是JVM&#xff08;Java虚拟机&#xff09;&#xff1f; 二、JVM的功能 三、JVM的功能-即时编译 四、常见的JVM 五、JVM的组成 五、JVM的工作流程 参考资料 一、什么是JVM&#xff08;Java虚拟机&#xff09;&#xff1f; 在Java的世界里&#xff0c;Java虚…

代码文档浏览器 Dash mac中文版软件特色

Dash mac是一个基于 Python 的 web 应用程序框架&#xff0c;它可以帮助开发者快速构建数据可视化应用。Dash 的工作原理是将 Python 代码转换成 HTML、CSS 和 JavaScript&#xff0c;从而在浏览器中呈现交互式的数据可视化界面。Dash 提供了一系列组件&#xff0c;包括图表、表…

如何将设置为静态IP的VMware虚拟机进行克隆以便可以复刻相应的环境

一定要关闭需要克隆的虚拟机右键要选择克隆的虚拟机&#xff0c;选择管理->克隆&#xff0c;进入克隆虚拟机向导 设定克隆出来的虚拟机名称以及位置&#xff0c;选择完成 克隆完成之后将会生成虚拟机&#xff0c;示例中生成的虚拟机为ubuntu-dev2 因为原本的虚拟机为静态ip的…

区域人员超限AI算法的介绍及TSINGSEE视频智能分析技术的行业应用

视频AI智能分析已经渗透到人类生活及社会发展的各个方面。从生活中的人脸识别、停车场的车牌识别、工厂园区的翻越围栏识别、入侵识别、工地的安全帽识别、车间流水线产品的品质缺陷AI检测等&#xff0c;AI智能分析技术无处不在。在某些场景中&#xff0c;重点区域的人数统计与…

Java集合拓展01

1、List&#xff0c;Set&#xff0c;Map三者的区别 List&#xff1a;一个有序&#xff08;元素存入集合的顺序和取出的顺序一致&#xff09;容器&#xff0c;元素可以重复&#xff0c;可以插入多个null元素&#xff0c;元素都有索引。常用的实现类有 ArrayList、LinkedList 和…

EMG肌肉信号处理合集 (一)

本文归纳了常见的肌肉信号预处理流程&#xff0c;方便EMG信号的后续分析。使用pyemgpipeline库 来进行信号的处理。文中使用了 UC Irvine 数据库的下肢数据。 目录 1 使用wrappers 定义数据类&#xff0c;来进行后续的操作 2 肌电信号DC偏置去除 3 带通滤波器处理 4 对肌电…

SpringCloud - 新版淘汰 Ribbon,在 OpenFeign 中整合 LoadBalancer 负载均衡

目录 一、LoadBalancer 负载均衡 1.1、前言 1.2、LoadBalancer 负载均衡底层实现原理 二、整合 OpenFeign LoadBalancer 2.1、所需依赖 2.2、具体实现 2.3、自定义负载均衡策略 一、LoadBalancer 负载均衡 1.1、前言 在 2020 年以前的 SpringCloud 采用 Ribbon 作为负载…

OOM问题排查+Jvm优化

OOM问题排查&#xff1a; 1、top命令&#xff1a;查看cpu和内存的使用情况。 2、jstat命令&#xff1a;查看YGC和FGC情况&#xff0c;一般都是老年代不够用。导致OOM 3、jmap命令&#xff1a; 查看哪个类的实例过多,以每个类占用多少了内存。4、jstack 查看线程与线程之间的阻…

80基于matlab的小波包熵与模糊C均值聚类的故障诊断,以凯斯西储大学轴承数据为例进行分析

基于matlab的小波包熵与模糊C均值聚类的故障诊断&#xff0c;以凯斯西储大学轴承数据为例进行分析。对数据进行小波包分解后重构&#xff0c;然后提取各频带能量分布&#xff0c;后计算小波包熵进行故障诊断。输出特征可视化结果。数据可更换自己的&#xff0c;程序已调通&…

Git远程库操作(GitHub)

GitHub 网址&#xff1a;https://github.com/ 创建远程仓库 远程仓库操作 命令名称作用git remote -v查看当前所有远程地址别名git remote add 别名 远程地址起别名git push 别名 分支推送本地分支上的内容到远程仓库git clone 远程地址将远程仓库的内容克隆到本地git pull 别…

基于STM32的色彩识别与分类算法优化

基于STM32的色彩识别与分类算法优化是一项与图像处理和机器学习相关的研究任务&#xff0c;旨在实现高效的色彩识别和分类算法在STM32微控制器上的运行。本文将介绍基于STM32的色彩识别与分类算法优化的原理和实现步骤&#xff0c;并提供相应的代码示例。 1. 色彩识别与分类概…

[SIGGRAPH-23] 3D Gaussian Splatting for Real-Time Radiance Field Rendering

pdf | proj | code 本文提出一种新的3D数据表达形式3D Gaussians。每个Gaussian由以下参数组成&#xff1a;中心点位置、协方差矩阵、可见性、颜色。通过世界坐标系到相机坐标系&#xff0c;再到图像坐标系的仿射关系&#xff0c;可将3D Gaussian映射到相机坐标系&#xff0c;通…

c语言:用迭代法解决递归问题

题目&#xff1a; 解释&#xff1a;题目的意思就是用迭代法的空间和时间复杂的太高了&#xff0c;需要我们减小空间与时间的复杂度&#xff0c;我就想到了迭代法&#xff0c;思路和代码如下&#xff1a; #include <stdio.h> //这里是递归法转迭代法 int main() {int x,i…

Rust语言特性探秘:宏的魔力

大家好&#xff01;我是lincyang。 今天我们继续深入探讨Rust语言中的一个有趣而强大的特性——宏&#xff08;Macros&#xff09;。 宏在Rust中扮演着特殊的角色&#xff0c;不仅提高了代码的灵活性&#xff0c;还增强了代码的可重用性。接下来&#xff0c;我们会通过具体的…

[ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务

本文收录于【#云计算入门与实践 - AWS】专栏中&#xff0c;收录 AWS 入门与实践相关博文。 本文同步于个人公众号&#xff1a;【云计算洞察】 更多关于云计算技术内容敬请关注&#xff1a;CSDN【#云计算入门与实践 - AWS】专栏。 本系列已更新博文&#xff1a; [ 云计算 | …