HashTable類也叫HashMap類,是Dictionary類的一種散列表實現方式。算法
散列算法的做用是儘量快的在數據結構中找到一個值。數據結構
在以前的學習中,若是你想要得到數據結構中的一個值,須要遍歷整個數據結構來找到它。若是使用散列函數,就能知道具體位置,也就可以快速找到該值。app
使用最多見的散列函數--‘lose lose’散列函數,簡單的將每一個鍵值中的每一個字母的ASCII碼值相加,將獲得的散列值做爲地址。函數
(鍵值)John (散列函數)74+111+104+110 (散列值)399 造成散列表學習
地址 | 數據鍵值對 |
---|---|
[399] | john/john@email.com |
... | |
[685] | gandalf/@email.com |
建立一個散列表this
function HashTable() { var table = []; }
實現一個散列函數,即將ASCII碼值相加的方法。指針
var loseloseHashTable = function(key) { var hash = 0; for(var i = 0; i < key.length; i++) { hash += key.charCodeAt(i); } return hash % 37; //這裏只是爲了獲得比較小的數值,隨便除了一個數 }
實現put(key, value)
方法,向散列表中添加一個新的項。code
this.put = function (key, value) { var position = loseloseHashTable(key); //獲得散列值,即位置 table[position] = value; };
實現get(key)
方法,返回根據鍵值檢索到的特定的值。索引
this.get = function(key) { var position = loseloseHashTable(key); return table[position]; };
實現remove()
方法,從散列中移除鍵值對應的數據值。ip
this.remove = function(key) { table[loseloseHashTable(key)] = undefined; //沒有數據佔據的位置默認值爲undefined };
具體使用方法這裏就不贅述了,就是方法的調用。
假若有多個鍵值獲得的散列值相等,那麼後面的元素會覆蓋前面前面相同散列值的元素,
怎麼解決呢?
分離連接
分離連接法爲散列表的每個位置建立一個鏈表並將元素存儲在裏面。
地址 | 鏈表存儲數據 |
---|---|
[5] | [no1/no1.com]指針-> [no2/no2.com]指針-> [X]null |
在地址5上,鏈表實例上有兩個元素no1.com和no2.com。
須要添加一個valuePair類,來表示將要加入鏈表的實例的元素。
var valuePair = function(key, value) { this.key = key; this.value = value; this.toString = function() { return `[${this.key} - ${this.value}]`; } }
重寫一個put()
方法
this.put = function(key, value) { var position = loseloseHashTable(key); if(table[position] == undefined) { table[position] = new LinkedList(); //若是這個位置是第一次被加入元素,那麼就初始化一個LinkedList實例 } table[position].append(new valuePair(key, value)); //鏈表實現的append方法添加一個valuePair實例。 };
重寫一個get(key)
方法
this.get = function(key) { var position = loseloseHashTable(key); if(table[position] !== undefined) { //位置上有元素存在 //遍歷鏈表來尋找鍵/值 var current = table[position].getHead(); while (current.next) { //這裏遍歷不到鏈表最後一個位置 if(current.element.key === key) { return current.element.value; //element屬性是valuePair的實例,包含key和value屬性 } current = current.next; } //檢查元素在鏈表第一個或者最後一個的狀況 if(current.element.key === key) { return current.element.value; } } return undefined; //位置上沒有值 };
重寫remove(key)
方法
this.remove = function(key) { var position = loseloseHashTable(key); if(table[position] !== undefined) { var current = table[position].getHead(); while (current.next) { if(current.element.key === key) { table[position].remove(current.element); //鏈表實現的remove方法 if(table[position].isEmpty()) { //刪除元素以後判斷鏈表是否變空 table[position] = undefined; } return true; } current = current.next; } //檢查是不是第一個或者最後一個元素 if(current.element.key === key) { table[position].remove(current.element); if(table[position].isEmpty()) { table[position] = undefined; } return true; } } return false; }
線性探查
若是索引爲index的位置已經被佔據了,就嘗試index+1的位置,以此類推。
5的位置被佔據,就尋找6的位置,6的位置被佔據,就找7,7沒被佔據就賦值(本應該在位置5上,可是線性探查變成了位置7)
實現put(key, value)
方法
this.put = function(key, value) { var position = loseloseHashTable(key); if(table[position] === undefined) { //這個位置沒有被佔據 table[position] = new valuePair(key, value); } else { var index = ++position; //尋找下一個位置 while(table[index] !== undefined) { //被佔據繼續尋找下一個位置 index ++; } table[index] = new valuePair(key, value); } };
實現get()
方法
this.get = function(key) { var position = loseloseHashTable(key); if(table[position] !== undefined) { if(table[position].key === key) { //舉例位置5 return table[position].value; //記號1 } else { var index = ++position; while(table[index] !== undefined && (table[index] && table[index].key !== key)) { //該位置有元素可是不是要尋找的元素 index ++; //索引增長 } if(table[index] && table[index].key === key) { //確認正確性 return table[index].value; //找到7 //記號2 } } } return undefined; };
實現remove()
方法
只須要改變get方法的記號1和記號2的位置代碼便可
改成table[index] = undefined
;
var djb2HashCode = function(key) { var hash = 5381; //初始化一個hash變量並賦值爲一個質數5381 for(var i = 0; i < key.length; i++) { //遍歷 hash = hash * 33 + key.charCodeAt(i); } return hash % 1013; }
相比來講,衝突會變少不少~