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.pswp.cn/news/161198.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161198.shtml
英文地址,請注明出處:http://en.pswp.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;不存在了&…

論防火墻的體系結構

防火墻的體系結構 防火墻的體系結構 雙重宿主主機體系結構。屏蔽主機體系結構。屏蔽子網體系結構。 雙重宿主主機體系結構 雙重宿主主機體系結構是指以一臺具有雙重宿主的主機計算機作為防火墻系統的主體&#xff0c;執行分離外部網絡與內部網絡的任務。該計算機至少有兩個…

【NGINX--4】大規模可擴展的內容緩存

1、緩存區 緩存內容并定義緩存的存儲位置。 使用 proxy_cache_path 指令定義共享內存緩存區和內容的位置&#xff1a; proxy_cache_path /var/nginx/cachekeys_zoneCACHE:60m levels1:2inactive3h max_size20g; proxy_cache CACHE;上述緩存定義示例在文件系統 /var/nginx/ca…

為什么要用多線程?

提高響應速度&#xff1a;對于耗時操作&#xff0c;使用多線程可以使得應用程序更快地響應用戶的請求&#xff0c;從而提高用戶體驗。實現并行計算&#xff1a;多線程可以同時執行多個任務&#xff0c;從而實現并行計算&#xff0c;提高程序的運行效率。提高CPU利用率&#xff…

html屬性值可以不用引號嗎,實例驗證

html屬性值可以不用引號 HTML元素的屬性值可以不適用引號來包裹&#xff0c;瀏覽器一樣可以將其進行渲染。不過&#xff0c;如果這樣寫HTML的代碼的話&#xff0c;屬性與屬性值之間需要用空格來進行隔開&#xff0c;避免后面的屬性變成前面屬性的屬性值。 提示&#xff1a;雖…

達夢列式存儲和clickhouse基準測試

要驗證達夢BigTable和ClickHouse的性能差異&#xff0c;您需要進行一系列基準測試。基準測試通常包括多個步驟&#xff0c;如準備測試環境、設計測試案例、執行測試、收集數據和分析結果。以下是您可以遵循的一般步驟&#xff1a; 準備測試環境&#xff1a; 確保兩個數據庫系統…

sql手工注入漏洞測試(MYSQL)-墨者-url信息

背景&#xff1a; 自己在墨者官網靶場練習的時候&#xff0c;一直出錯&#xff0c;手工容易出錯&#xff0c;所以列舉一些信息供大家核對&#xff0c;可以參考改動。 數據庫版本version() 5.7.22-0ubuntu0.16.04.1 當前數據庫名稱database&#xff08;) m…

模擬量采集----測量輸入的電流

生活中的模擬量有很多 大多都為電壓信號和電流信號 今天講如何測量輸入的電流信號 通過歐姆定律可知 電流測量的測量&#xff1a;是將電流加載在固定阻值的電阻上&#xff0c;來測量這個電阻二端的電壓 最后反算出電流的大小 所用的公式是IU/R 我們使用仿真軟件來看測量…

神經網絡中間層特征圖可視化(輸入為音頻)(二)

相比方法一個人感覺這種方法更好 import librosa import numpy as np import utils import torch import matplotlib.pyplot as pltclass Hook:def __init__(self):self.features Nonedef hook_fn(self, module, input, output):self.features output# 創建鉤子的實例 hook …

EasyExcel listener無法通過Autowired注入xxMapper

easyexcel listener無法通過Autowired注入xxMapper 文章目錄 easyexcel listener無法通過Autowired注入xxMapperbug記錄&#xff1a;解決方案&#xff1a;easyexcel 使用例子controllerServiceImpllistener bug記錄&#xff1a; productMapper注入一直為null,而procureDetailM…

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;特別是…

連續變量降維:主成分分析和因子分析

主成分分析&#xff08;Principal Component Analysis&#xff0c;PCA&#xff09;和因子分析&#xff08;Factor Analysis&#xff09;都是用于處理連續變量降維的統計方法&#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;重點區域的人數統計與…

3:kotlin 邏輯控制(Control flow)

向其他語言一樣&#xff0c;kotlin也有循環和邏輯控制 條件判斷&#xff08;Conditional expressions&#xff09; kotlin使用if和when來進行條件判斷 如果糾結選擇if還是when&#xff0c;建議使用when&#xff0c;因為它更能提高程序的健壯性 if 普通寫法 fun main() {val…

Java集合拓展01

1、List&#xff0c;Set&#xff0c;Map三者的區別 List&#xff1a;一個有序&#xff08;元素存入集合的順序和取出的順序一致&#xff09;容器&#xff0c;元素可以重復&#xff0c;可以插入多個null元素&#xff0c;元素都有索引。常用的實現類有 ArrayList、LinkedList 和…