1. 定義
字典:使用[鍵,值]
的形式儲存資料。儲存不重複的值。字典也稱作對映
。
2. 字典例項(Map)
在Es6中,已經包含了一個Map類的實現,也就是所說的字典。這裡是自己實現一個Map類。
- 使用一個Object例項,而不是陣列來儲存元素
- set(key, value): 像字典中新增元素
- remove(key): 通過鍵值一處鍵值對應的資料值
- has(key): 是否包含該key,存在返回true,否則返回false
- get(key): 根據鍵,獲取對於的值並返回
- clear(): 將字典中的元素全部刪除
- size(): 獲取字典元素數量
- keys(): 將字典包含的所有鍵以陣列的形式返回
- values(): 將字典包含的所有值以陣列的形式返回
function Map() {
let items = {};
// 是否存在key
this.has = (key) => (key in items);
// 新增或更新:key,value
this.set = (key, value) => {
items[key] = value;
};
// 刪除值
this.remove =(key) => {
if (this.has(key)) {
delete items[key];
return true;
}
return false;
};
// 獲取值
this.get = (key) => {
return this.has(key) ? items[key] : undefined;
};
// 清除所有
this.clear = () => {
items = {};
}
// size(): 獲取字典元素數量
this.size = () => {
let length = 0;
for(let item in items) {
if (items.hasOwnProperty(item)) {
length++;
}
}
return length;
};
// 將字典包含的所有鍵以陣列的形式返回
this.keys = () => {
let ret = [];
for(let item in items) {
if (items.hasOwnProperty(item)) {
ret.push(item);
}
}
return ret;
}
// 將字典包含的所有值以陣列的形式返回
this.values = () => {
let ret = [];
for(let item in items) {
if (items.hasOwnProperty(item)) {
ret.push(items[item]);
}
}
return ret;
}
}
複製程式碼
3. HashMap 定義
HashMap 也是一種Dictionary類的一種雜湊表實現方式。
雜湊演算法: 是為了儘可能快地在資料結構中找到一個值。
3.1 雜湊函式
使用雜湊函式,可以知道值的具體位置,能夠快速地檢索到該值,雜湊函式的作用是:給定一個鍵值,然後返回值在表中的地址。
我們有一組資料,某個人的郵箱地址:
{
Gandalf: 'gandalf@email.com',
John: 'johnsnow@emal.com',
Tyrion: 'tyrion@email.com'
}
複製程式碼
- 我們使用雜湊函式,將每個鍵中的字母相加,計算出
雜湊值
(可以想象為地址索引) - 在雜湊表中:以雜湊值為儲存索引,並儲存相應的value。那麼我們在通過key進行查詢value時,就不再需要一個一個迴圈,而直接根據雜湊值獲取。
4. 雜湊表例項
和之前一樣,我們需要準備資料儲存物件,新增相應的方法:
- 我們將使用陣列來表示資料結構。
- getHashCodeKey(key): 根據key生成雜湊表的key。該方法不會暴露到外部
- put(key, value): 向雜湊表增加一個新的項
- remove(key): 根據鍵從雜湊表中移除值
- get(key): 返回根據鍵值檢索的特定的值
function HashTable() {
// 使用陣列儲存資料
let table = [];
// 根據key生成雜湊表的key
function getHashCodeKey(key) {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
}
// 新增雜湊資料
this.put = (key, value) => {
let position = getHashCodeKey(key);
console.log(position + ' - ' + key);
table[position] = value;
};
// 獲取資料
this.get = function (key) {
return table[getHashCodeKey(key)];
};
// 移除
this.remove = function(key) {
table[getHashCodeKey(key)] = undefined;
};
}
複製程式碼
5. 雜湊集合
雜湊集合是由一個集合構成,但是插入、移除、獲取元素時,使用的是雜湊函式。我們可以重用上面所有的程式碼,區別在於:我們新增的key值就是插入的值:[value轉化為key, value]。和集合相似,雜湊集合之儲存唯一不重複
的值。
6. 雜湊表衝突
按照上面的順序,會出現相同key的情況,不同的值在雜湊表中對應相同位置的時候表示衝突了。解決該問題可用以下方法:
- 分離連結(需要重寫get,remove,put方法):
function HashTable() {
let items = [];
// 連結串列每一個物件對應的item類
function ValuePair(key, value) {
this.key = key;
this.value = value;
this.toString = () => {
console.log(`[${this.key} - ${this.value}]`)
};
}
this.put = (key, value) => {
let position = getHashCodeKey(key);
if (table[position] === undefined) {
table[position] = new LinkedList();
}
table[position].append(new ValuePair(key, value));
};
this.get = function(key) {
var position = getHashCodeKey(key);
if (table[position] !== undefined){
//遍歷連結串列來尋找鍵/值
var current = table[position].getHead();
while(current.next){
if (current.element.key === key){
return current.element.value;
}
current = current.next;
}
//檢查元素在連結串列第一個或最後一個節點的情況
if (current.element.key === key){
return current.element.value;
}
}
return undefined;
};
this.remove = function(key){
var position = getHashCodeKey(key);
// 如果位置為空,那麼不存在該元素,返回false
if (table[position] !== undefined){
// 獲取該位置的連結串列的head
var current = table[position].getHead();
// 連結串列有不只一個item, 通過迴圈遍歷查詢後進行移除
while(current.next){
if (current.element.key === key){
table[position].remove(current.element);
// 刪除後,如果連結串列長度為0,則從雜湊表中移除該key
if (table[position].isEmpty()){
table[position] = undefined;
}
return true;
}
current = current.next;
}
// 連結串列只有一個item: 檢查是否為第一個或最後一個元素
if (current.element.key === key){
table[position].remove(current.element);
if (table[position].isEmpty()){
table[position] = undefined;
}
return true;
}
}
return false;
};
}
複製程式碼
- 線性探查(需要重寫get,remove,put方法)
當想向表中某個位置加入一個新元素的時候,如果索引為index的位置已經被佔據了,就嘗試index+1的位置。如果index+1的位置也被佔據了,就嘗試index+2的位置,以此類推。
7. 建立更好的雜湊函式
上面的雜湊函式會產太多的衝突,應該儘量避免衝突。
var djb2HashCode = function (key) {
// 初始化一個hash值,並賦值為一個質數
var hash = 5381;
// 迭代key,將hash與33相乘,並和當前迭代到的字元的ASCII碼值相加
for (var i = 0; i < key.length; i++) {
hash = hash * 33 + key.charCodeAt(i);
}
// 最後將hash與另一個隨機質數相除的餘數
return hash % 1013;
};
複製程式碼