js實現資料結構及演算法之雜湊表(Hashtable)

eternalless發表於2018-08-31

雜湊表(Hashtable)

雜湊表也被稱為雜湊表,Hash表是一種特殊的資料結構。

雜湊後的資料 可以快速插入和取用

在雜湊表上插入、刪除和取用資料非常快,但是查詢資料卻效率低下

js雜湊表基於陣列設計,理想情況雜湊函式會將每一個鍵值對映為唯一的陣列索引,陣列長度有限制,更現實的策略是將鍵均勻分佈

陣列長度是預先設定的,可以隨時增加,所有元素根據和該元素對應的,儲存陣列特定的位置

即使使用高效的雜湊函式,依然存在兩個鍵值相同的情況,這種現象稱為碰撞(collision)

陣列的長度應該是一個質數,所有的策略都基於碰撞

js實現資料結構及演算法之雜湊表(Hashtable)

碰撞解決方法

開鏈法:兩個鍵相同儲存位置一樣,開闢第二陣列,也稱第二個陣列為鏈,適合空間小,資料量大

線性探測法屬於開放定址雜湊,查詢雜湊位置如果當前位置沒有繼續尋找下一個位置。儲存資料較大較適合。陣列大小>=1.5*資料(開鏈法),陣列大小>=2*資料(線性探測法)

js實現

 /**
  * 一個簡單的雜湊表
  * @constructor
  */
 function HashTable() {
this.table = new Array(137);          // 定義陣列長度
this.simpleHash = simpleHash;         // 簡單的雜湊函式
this.betterHash = betterHash;         // 簡單的雜湊函式
this.showDistro = showDistro;         // 顯示元素
this.put = put;                       // 插入元素
this.openPut = openPut;               // 開鏈法插入元素
this.linkPut = linkPut;               // 線性探測法插入元素
this.get = get;                       // 獲取元素
this.bulidTable = bulidTable          //新增二維陣列
}
//簡單的雜湊函式 除留餘數法
 function simpleHash(data) {
   var total = 0
   for(var i = 0; i < data.length; i++){
     total += data.charCodeAt(i)
   }
   console.log(data + '->total' + total)
   return total % this.table.length
 }
 //顯示元素
 function showDistro() {
   for(var i = 0; i < this.table.length; i++) {
     if(this.table[i]  !== undefined) {
       console.log('鍵值是->' + i + '值是' + this.table[i])
     }
   }
 }
 //插入元素
 function put(data) {
  var pos = this.simpleHash(data)
   this.table[pos] = data
 }

 //獲取元素
 function get(data) {
   return this.table[this.simpleHash(data)]
 }

 var ht = new HashTable()
 ht.put('abc')
 ht.put('china')
 ht.put('bbb')
 ht.put('ss')
 ht.put('nicah')
 ht.put('cba')
 ht.showDistro()複製程式碼

可以看到我們插入6個值,最後只顯示3個,原因是發生了碰撞

js實現資料結構及演算法之雜湊表(Hashtable)

解決方法1:改造hash函式

//改造後的雜湊函式
function betterHash(data) {
  var h = 31
  var total = 0
  for(var i = 0; i < data.length; i++){
    total += h*total + data.charCodeAt(i)
  }
  console.log(data + '->total' + total)
  return total % this.table.length
}
//更改put
function put(data) {
 var pos = this.betterHash(data) //使用betterHash
  this.table[pos] = data
}
複製程式碼

可以看到其他元素可以顯示出來了,但是新增相同元素的時候,不顯示

js實現資料結構及演算法之雜湊表(Hashtable)

解決方法2:開鏈法

在建立儲存雜湊過的鍵值陣列時,建立一個新的空陣列,然後將該陣列付給雜湊表中的每個陣列元素,這樣建立了一個二維陣列,也稱第二個陣列為鏈。

//新增二維陣列
function bulidTable() {
  for(var i = 0; i<this.table.length; i++){
    this.table[i] = new Array()
  }
}//開鏈法插入元素
function openPut(data) {
  var pos = this.simpleHash(data)
  var index = 0
  if(this.table[pos][index] === undefined) {
    this.table[pos][index] = data
    index ++
  }else {
    while(this.table[pos][index] !== undefined) {
       ++index
    }
    this.table[pos][index] = data
  }
}
//顯示元素更改
function showDistro() {
  for(var i = 0; i < this.table.length; i++) {
    if(this.table[i][0] !== undefined) {
      console.log('鍵值是->' + i + '值是' + this.table[i])
    }
  }
}
//插入元素更改為simpleHash
function put(data) {
 var pos = this.simpleHash(data)
 this.table[pos] = data
}

var ht = new HashTable()
ht.bulidTable()
ht.openPut('abc')
ht.openPut('china')
ht.openPut('bbb')
ht.openPut('ss')
ht.openPut('nicah')
ht.openPut('cba')
ht.openPut('cba')
ht.showDistro()複製程式碼

可以看到所有元素都被顯示出來了

js實現資料結構及演算法之雜湊表(Hashtable)

解決方法3:線性探測法(定址法)

當發生碰撞時,檢測下一個位置是否為空。如果為空,就將此資料存入該位置;如果不為空,則繼續檢查下一個位置,直到找到下一個空的位置為止。

//線性探測法
function linkPut(data) {
  var pos = this.simpleHash(data)
  if(this.table[pos] === undefined) {
    this.table[pos] = data
  }else {
    while(this.table[pos] !== undefined) {
      pos++
    }
    this.table[pos] = data
  }
}
//顯示元素
function showDistro() {
  for(var i = 0; i < this.table.length; i++) {
    if(this.table[i]  !== undefined) {
      console.log('鍵值是->' + i + '值是' + this.table[i])
    }
  }
}

var ht = new HashTable()
// ht.bulidTable()
ht.linkPut('abc')
ht.linkPut('china')
ht.linkPut('bbb')
ht.linkPut('ss')
ht.linkPut('nicah')
ht.linkPut('cba')
ht.linkPut('cba')
ht.showDistro()複製程式碼

可以看到所有元素都被顯示出來了

js實現資料結構及演算法之雜湊表(Hashtable)

至此,算是完成了雜湊表


相關文章