hashmap赋值给另一个hashmap_图解设计一个 HashMap
目前我們學(xué)到的數(shù)據(jù)結(jié)構(gòu)有:單鏈表,雙向鏈表,棧,隊(duì)列,循環(huán)隊(duì)列,雙端隊(duì)列。今天學(xué)習(xí) LeetCode 的 「 706. Design HashMap 」,從設(shè)計(jì)一個(gè) HashMap 到掌握其內(nèi)部原理。題目要求:
Design a HashMap without using any built-in hash table libraries.
To be specific, your design should include these functions:put(key, value) : Insert a (key, value) pair into the HashMap. If the value already exists in the HashMap, update the value.get(key): Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key.remove(key) : Remove the mapping for the value key if this map contains the mapping for the key.
? All keys and values will be in the range of [0, 1000000].
The number of operations will be in the range of?[1, 10000].
題目要求設(shè)計(jì)一個(gè) HashMap,不能使用語言提供的類似哈希表的庫,比如HashMap,dict,map,需要實(shí)現(xiàn)下面幾個(gè)方法:
1. put(key, value) : 根據(jù) key 插入一個(gè) value,如果 key 已經(jīng)存在,更新 value;
2. get(key): 根據(jù)一個(gè) key 獲取對應(yīng)的值,如果未找到對應(yīng)的 value,返回 -1;
3. remove(key) : 根據(jù) key 來刪除對應(yīng)的值。
分析
HashMap 是一種典型的以空間換取時(shí)間的數(shù)據(jù)結(jié)構(gòu),在設(shè)計(jì)緩存算法 LRU 和 LFU 當(dāng)中用到了 C++ 提供的 unorder_map,利用HashMap 的特點(diǎn),做到存取時(shí)間復(fù)雜度為 O(1) 。今天我們要掌握如何設(shè)計(jì)一個(gè) HashMap。設(shè)計(jì)之前需要知道?HashMap 是一種什么樣的數(shù)據(jù)結(jié)構(gòu)?
HashMap 的核心思想是 「 把 key 通過一種方式轉(zhuǎn)換成一個(gè) hashCode(一個(gè)整形數(shù)),通過 hashCode 來存取對應(yīng)的 value 」,轉(zhuǎn)換的方式就是哈希函數(shù),在轉(zhuǎn)換的過程中,不同的 key 可能會生成同一 hashCode,這將產(chǎn)生「哈希沖突」。
一圖勝千言!
插入 key 對應(yīng)的 value 函數(shù)為:put(key, value):
執(zhí)行 put(1, 1),1 的 hash 值 hash(1) = 1 % 5 = 1,放到 1 個(gè)位置;
執(zhí)行 put(4, 4),4 的 hash 值 hash(4) = 4 % 5 = 4,放到 4 個(gè)位置;
執(zhí)行 put(6, 6),6 的 hash 值 hash(6) = 6 % 5 = 1,放到 1 個(gè)位置,第一個(gè)位置已經(jīng)存放了 1,產(chǎn)生「哈希沖突」;
綜上,設(shè)計(jì)一個(gè)哈希表需要做下面 2 件事:
1.設(shè)計(jì)哈希函數(shù);
衡量一個(gè)哈希函數(shù)設(shè)計(jì)的好壞是看它是否能夠讓 value 「均勻分布」,也就是產(chǎn)生哈希沖突越少越好。語言本身一般會提供計(jì)算 hashCode 的方法,比如 OC 中的 NSObject 類提供了 hash 方法:
NSString *name = @"Lefe_x";NSString?*des?=?@"超越技術(shù)公眾號做圖解算法";NSLog(@"hash(name)?=?%@,?hash(des)=%@",?@(name.hash),?@(des.hash));// hash(name) = 7306077673678745, hash(des)=77237046174833269552.解決哈希沖突;
不同的 key 生成的 hashCode 相同就產(chǎn)生了哈希沖突,解決沖突有主要有下面幾種方式:
鏈地址法:產(chǎn)生哈希沖突后,把產(chǎn)生沖突的元素使用某種方式「組合」到一起,可以使用鏈表、紅黑樹,或者使用其它數(shù)據(jù)結(jié)構(gòu)。
把 1、6、3、4、13 分別 put 到哈希表中,1、6、13 的哈希值均為 1,被放到第一個(gè)位置,可以通過鏈表、紅黑樹進(jìn)行存儲。
開地址法:產(chǎn)生哈希沖突后,把 value 放到其它空閑位置,可以使用線性探測法放到下一個(gè)空閑位置;使用平方探測法,放到第1個(gè)、第 4個(gè)、第9個(gè)、第16個(gè)......空閑位置;使用二次哈希,通過另外一個(gè)哈希函數(shù)再計(jì)算一次哈希值。
使用線性探測法解決沖突,把 1、6、3、4、13 分別 put 到哈希表中,1、6、13 的哈希值均為 1,會產(chǎn)生沖突,當(dāng)遇到?jīng)_突后,把 value 插入到下一個(gè)位置。保存結(jié)果如下圖:
代碼
通過上面的分析可知,實(shí)現(xiàn)一個(gè) HashMap,需要一個(gè)實(shí)現(xiàn)一個(gè)哈希函數(shù)和解決哈希沖突,代碼中通過「鏈地址法」來解決哈希沖突。題目中的 key 和 value 的取值范圍為 [ 0 - 1000000 ]。代碼原理如圖:
C++ 代碼如下(來源于 LeetCode):
#include #include #include using?namespace?std;class MyHashMap { size_t m_size = 10000; vector<listint, public: // 初始化,設(shè)置大小 MyHashMap() { m_data.resize(m_size); } // 哈希函數(shù) int hashCode(int key) { return key % m_size; } // 根據(jù) key 存儲對應(yīng)的 value,如果 key 已經(jīng)存在,更新 value void put(int key, int value) { // 根據(jù)哈希函數(shù)找到對應(yīng)的鏈表 auto &list = m_data[hashCode(key)]; for (auto & val : list) { // 如果已經(jīng)存在,根據(jù) key 來更新對應(yīng)的值 if (val.first == key) { val.second = value; return; } } // 插入鏈表的尾部 list.emplace_back(key, value); } // 根據(jù) key 來獲取值 int get(int key) { const auto &list = m_data[hashCode(key)]; if (list.empty()) { return -1; } for (auto & val : list) { // 如果已經(jīng)存在,找到了對應(yīng)的值 if (val.first == key) { return val.second; } } return -1; } // 根據(jù) key 刪除對應(yīng)的值 void remove(int key) { auto &list = m_data[hashCode(key)]; // 找到節(jié)點(diǎn)后刪除 list.remove_if([key](auto n) { return n.first == key; }); }};總結(jié)
至此,一個(gè)簡單的 HashMap 就完成了,如果設(shè)計(jì)一個(gè)復(fù)雜的 HashMap,需要考慮數(shù)據(jù)達(dá)到一定程度后,需要對 vector 進(jìn)行擴(kuò)容、縮容處理,如果沖突達(dá)到某一個(gè)量級后,需要考慮更換 list 這個(gè)數(shù)據(jù)結(jié)構(gòu),比如換成紅黑樹。
推薦閱讀:
論證:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)和算法很重要
圖解 LFU cache
圖解數(shù)據(jù)結(jié)構(gòu)和算法
總結(jié)
以上是生活随笔為你收集整理的hashmap赋值给另一个hashmap_图解设计一个 HashMap的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux下hg无法运行_千万不要运行的
- 下一篇: 群晖nas怎么上传整个文件夹_你为什么需