前言:
數組進行插入的時候,效率比較低
數組進行查找的時候
1.如果基於下標 則很快O(1)
2.如果基於內容去查找,則很低
數組進行刪除的時候,效率低
數組進行修改的時候,
1.如果基於下標 則很快O(1)
2.如果基於內容,則很低
哈希表通常是基於數組實現的
優勢:
插入-刪除-查找 巨快
查找的速度比樹還要快
編碼比數簡單
劣勢:
key不能重復,數值存放是無序的。
實現的關鍵地方:
1.數組下標值的轉換。通過哈希函數實現
獲取index。
* 1.將字符串轉換成比較大的數字,這個數字稱為hashCode
* 2.將大的數字壓縮到數組(size)范圍之內既index
公式:
hashCode = hashCode * prime + key.charCodeAt(i);(快速計算)
var index = hashCode % size;(霍納法則
prime為質數,且小於數組的容量。
size為哈希表的長度
2.哈希表擴容
*1.為什么要擴容?
哈希表中存儲的元素個數不夠存放,或者鏈地址法長度很長。
*2.注意點
一旦擴容,所有數據都要重新插入。
理由:
求余運算,例如12=2,但是如果12%20呢 那就為12了。
*3.什么情況下擴容/縮容
loadFactor = 表中元素個數/表的長度
loadFactor > 0.75 擴容
loadFactor < 0.25 縮容
每次插入或則刪除鍵值對的時候,就進行一次判斷。
好的哈希函數的特點:
1.快速的計算(通過簡單的運算就可以得到hashCode)
方法:霍納法則
2.均勻的分布
需要使用常量的地方盡量使用質數
例如哈希表的長度為質數(index)
2.解決沖突(既是存放數據如果下標重合的解決方法)
2.1鏈地址法
存放數據的數組里面每一個下標不在是單純的基本數據類型,而是引用類型。
引用類型有兩種:
鏈表結構
數組結構
2.2開放地址法
尋找哈希表里面空白的下標來添加重復的數據
查找重復的數據
找到之后線性往下查到,遇到空值返回
刪除數據
考慮到查找數據的問題,所以刪除數據時將數據賦值為-1。避免重復的數據因為空值查找不到
/** * 哈希表 鏈地址法 */ function HashTable() { this.storage = []; // 存放元素 this.count = 0; // 存放的元素個數 this.limit = 7; // 哈希表的長度 盡量為質數 /** * 哈希函數 * 作用:返回key在哈希表的中的下標位置 * 參數:key(String) size(哈希表長度) */ HashTable.prototype.hashFunc = function (key, size) { // 1.定義hashCode變量 var hashCode = 0; // 2.霍納法則計算hashCode的值 for (var i = 0; i < key.length; i++) { var zishu = 37; // 賦值成質數 hashCode = hashCode * zishu + key.charCodeAt(i); } // 3.取余操作 var index = hashCode % size; return index; } /** * 作用:添加或者修改操作 * 參數:key(String) value(Any) */ HashTable.prototype.push = function(key,value) { // 1.根據key獲取index var index = this.hashFunc(key,this.limit); // 2.根據index獲取bukcet var bucket = this.storage[index]; // bucket 代表存儲相同key的桶 // 3.判斷bucket是否存在,如果不存在 則賦值為[] if(bucket == null) { bucket = []; this.storage[index] = bucket; } // 4.判斷bucket里面的tuple是修改還是添加 for(var i = 0; i < bucket.length; i++ ) { var temp = bucket[i]; if(temp[0] == key) { temp[1] = value; return; // 這里判斷為修改 直接結束該方法 } } bucket.push([key,value]); // 添加 // 5.當前哈希表元素個數+1 this.count ++; // 6.判斷是否需要擴容 var loadFactor = this.count / this.limit; if( loadFactor > 0.75) { var newLimit = this.limit*2; while(!this.isPrime(newLimit)) { newLimit++; } this.reSize(newLimit); } } /** * 作用:根據key獲取value * 參數: key(String) * 返回:找的到返回value 找不到返回null */ HashTable.prototype.get = function(key) { //1.根據key獲取index var index = this.hashFunc(key,this.limit); //2.根據index獲取bucket(array) var bucket = this.storage[index]; //3.判斷bucket是否存在 if(bucket == null) { return null; } //4.根據key從bucket中獲取tuple(數組) var tuple = []; for(var i = 0; i < bucket.length; i++) { tuple = bucket[i]; if(tuple[0] == key) { break; } } //5.根據tuple返回結果 return tuple.length == 0 ? null : tuple[1]; } /** * 作用:根據key刪除一個truple(array) * 參數: key(String) * 返回: 刪除的元素的value */ HashTable.prototype.remove = function(key) { // 1.根據key找到index var index = this.hashFunc(key,this.limit); // 2.根據index獲取bucket var bucket = this.storage[index]; // 3.判斷bucket是否存在 if(bucket == null) return // 4.根據key從bucket中刪除tuple(數組) for(var i = 0; i < bucket.length; i++) { var tuple = bucket[i]; if(tuple[0] == key) { // delete tuple; // 這種可行否? 不行,delete 只對對象的屬性有效 bucket.splice(i,1); break; } } // 5.哈希表長度-1 this.count--; // 6.判斷是否需要縮容 var loadFactor = this.count / this.limit; if( loadFactor < 0.25 && this.limit < 7) { var newLimit = parseInt(Math.floor(limit/2)); while(!this.isPrime(newLimit)) { newLimit++; } this.reSize(newLimit); } return tuple[1]; } /** * 判斷哈希表是否為空 */ HashTable.prototype.isEmpty = function(){ return this.count == 0 ? true : false; } /** * 判斷哈希表元素的個數 */ HashTable.prototype.size = function(){ return this.count } /** * 哈希表擴容 * 參數limit哈希表新的長度。 */ HashTable.prototype.reSize = function(limit){ //1.創建oldStorage指向當前storage var oldStorage = this.storage; //2.重置當前鏈表屬性 this.storage = []; this.limit = limit; this.count = 0; //3.逐個賦值 for(var i = 0; i < oldStorage.length; i++){ var bucket = oldStorage[i]; if(bucket==null) { continue; } for(var j = 0; j < bucket.length; j++) { var tuple = bucket[j]; this.push(tuple[0],tuple[1]); } } } /** * 判斷是否為質數 * 參數num */ HashTable.prototype.isPrime = function(num) { var temp = parseInt(Math.sqrt(num)) for(var i = 2; i <= temp;i++ ) { if(num%i == 0) { return false; } } return true } /** * 打印哈希表 */ HashTable.prototype.toString = function(){ for (const key in this.storage) { if (this.storage.hasOwnProperty(key)) { let bucket = this.storage[key]; let str = ""; for (const key1 in bucket) { if (bucket.hasOwnProperty(key1)) { let tuple = bucket[key1]; str = str + " " + tuple[1]; } } console.log(str); } } } } var hash = new HashTable(); hash.push("我","wo") hash.push("是","shi") hash.push("你","ni") hash.push("你3","ni1") hash.push("你1","ni2") hash.push("你3","ni3") hash.push("你5","ni5") hash.push("你6","ni6") hash.push("你8","ni8") hash.push("你9","ni9") hash.push("你01","ni10") hash.push("你1","ni8") hash.push("你3","ni9") hash.push("你9","ni10") hash.toString(); console.log("------------------------------------") hash.remove("是"); hash.toString();