摘要:小結實現了字典和哈希表感覺沒有想象中那么困難,當然這還是開始。
字典本系列所有文章:
第一篇文章:學習數據結構與算法之棧與隊列
第二篇文章:學習數據結構與算法之鏈表
第三篇文章:學習數據結構與算法之集合
第四篇文章:學習數據結構與算法之字典和散列表
第五篇文章:學習數據結構與算法之二叉搜索樹
不是新華字典哦,這里的字典也稱作_映射_,是一種數據結構,跟set集合很相似的一種數據結構,都可以用來存儲無序不重復的數據。不同的地方是集合以[值,值]的形式存儲,而字典則是以[鍵,值]或者叫作{key: value}的形式。
用JavaScript實現字典先實現一個構造函數:
function Dictionary () { var items = {} }
字典需要實現以下方法:
set(key, value):向字典中添加新元素
remove(key):通過使用key來從字典中移除對應元素
has(key):通過key來判斷字典中是否有該元素
get(key):通過key來找到特定的數值并返回
clear():清空字典
size():返回字典包含元素的數量
keys():返回字典所包含的所有鍵名,以數組形式返回
values():同上一個方法一樣,只不過鍵名換成了數值,也是以數組形式返回
實現has因為后面的方法都要用到has,所以先實現這個方法
this.has = function (key) { // 書上用的是in操作符來判斷,但是in對于繼承來的屬性也會返回true,所以我換成了這個 return items.hasOwnProperty(key) }實現set
沒啥好說的,簡單的賦值
this.set = function (key, value) { items[key] = value }實現remove
首先判斷key是否屬于該字典,然后再刪除
this.remove = function (key) { if (this.has(key)) { delete items[key] return true } return false }實現values
返回由數值組成的數組
this.values = function () { var values = [] for (var k in items) { if (items.has(k)) { values.push(items[k]) } } return values }實現其他的方法
其他的比較簡單,和集合的方法實現類似,我就直接貼源代碼了
this.keys = function () { return Object.keys(items) } this.size = function () { return Object.keys(items).length } this.clear = function () { items = {} } this.getItems = function () { return items } this.get = function (key) { return this.has(key) ? items[key] : undefined }
源代碼在此:
散列表字典的實現-源代碼
關于散列表的定義,這里摘抄一下維基百科的解釋:
散列表(Hash table,也叫哈希表),是根據鍵(Key)而直接訪問在內存存儲位置的數據結構。也就是說,它通過計算一個關于鍵值的函數,將所需查詢的數據映射到表中一個位置來訪問記錄,這加快了查找速度。這個映射函數稱做散列函數,存放記錄的數組稱做散列表。
可以理解為,數據中的鍵通過一個散列函數的計算后返回一個數據存放的地址,因此可以直接通過地址來訪問它的值,這樣的查找就非常快。
用JavaScript實現散列表先看這個構造函數,注意:存儲數據用的是數組,因為尋址訪問元素最方便的還是數組了。
function HashTable () { var table = [] }
需要實現的方法:
put(key, value):向散列表增加一個新的項
remove(key):根據鍵值從散列表中移除值
get(key):返回根據鍵值檢索到的特定的值
先實現散列函數把鍵名的每個字母轉成ASCII碼再相加起來,最后和一個任意的數求余,得到數據存儲的地址。
var loseloseHashCode = function (key) { var hash = 0 for (var i = 0; i < key.length; i++) { hash += key.charCodeAt(i) } return hash % 37 }簡單實現
因為這里的方法比較簡單,我就直接全貼上來了
this.put = function (key, value) { var position = loseloseHashCode(key) console.log(position + " - " + key) table[position] = value } this.remove = function (key) { table[loseloseHashCode(key)] = undefined } this.get = function (key) { return table[loseloseHashCode(key)] } // 用來輸出整個散列表,查看結果用的 this.print = function () { for (var i = 0; i < table.length; i++) { if (table[i] !== undefined) { console.log(i + ": " + table[i]) } } }
稍等,還沒完呢,現在看似很完美,我們調用一下剛才寫的:
var hash = new HashTable() hash.put("Gandalf", "gandalf@email.com") hash.put("John", "johnsnow@email.com") hash.put("Tyrion", "tyrion@email.com") // 輸出結果 // 19 - Gandalf // 29 - John // 16 - Tyrion
好像沒什么不對,但是仔細想想:如果在某些情況下,散列函數根據傳入的鍵計算得到的地址是一樣的會怎么樣呢?
看看下面這個例子:
var hash = new HashTable() hash.put("Gandalf", "gandalf@email.com") hash.put("John", "john@email.com") hash.put("Tyrion", "tyrion@email.com") hash.put("Aaron", "aaron@email.com") hash.put("Donnie", "donnie@email.com") hash.put("Ana", "ana@email.com") hash.put("Jonathan", "jonathan@email.com") hash.put("Jamie", "jamie@email.com") hash.put("Sue", "sue@email.com") hash.put("Mindy", "mindy@email.com") hash.put("Paul", "paul@email.com") hash.put("Nathan", "nathan@email.com") // 輸出結果 // 19 - Gandalf // 29 - John // 16 - Tyrion // 16 - Aaron // 13 - Donnie // 13 - Ana // 5 - Jonathan // 5 - Jamie // 5 - Sue // 32 - Mindy // 32 - Paul // 10 - Nathan
這種情況就比較復雜了:Tyrion和Aaron的值都是16,Donnie和Ana都是13,還有其他很多重復的值,這時散列表表中是什么情況呢
hash.print() // 輸出結果 // 5: sue@email.com // 10: nathan@email.com // 13: ana@email.com // 16: aaron@email.com // 19: gandalf@email.com // 29: john@email.com // 32: paul@email.com
很明顯,后面的元素會覆蓋前面的元素,但這樣是不行的,要想辦法解決沖突
解決沖突目前主流的方法主要是兩種:分離鏈接法和線性探查法,這里就簡單介紹一下分離鏈接法。
分離鏈接法思路很簡單:你不是重復了嗎,那我就在同一個位置里面放一個鏈表,重復的數據全都放在鏈表里面,你要找數據就在鏈表里面找。
如果不理解,可以參見下圖:
(圖片來源谷歌搜索,侵刪)
根據這個思路,我們重新實現一下三個方法,不過在這之前,我們需要一個對象來保存鍵值對
var ValuePair = function (key, value) { this.key = key this.value = value // 重寫toString主要是方便輸出查看 this.toString = function () { return "[" + this.key + " - " + this.value + "]" } }
接下來就是重寫方法了
this.put = function (key, value) { var position = loseloseHashCode(key) // 如果發(fā)現該位置還沒有元素,就先放一個鏈表,再用append加進去 if (table[position] === undefined) { // 因為使用node執(zhí)行文件,這里LinkedList是我在頂部用require引入的LinkedList.js table[position] = new LinkedList() } table[position].append(new ValuePair(key, value)) } this.get = function (key) { var position = loseloseHashCode(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 = loseloseHashCode(key) if (table[position] !== undefined) { var current = table[position].getHead() // 遍歷查找值 while (current.next) { if (current.element.key === key) { // 使用鏈表的remove方法 table[position].remove(current.element) // 當鏈表為空了,就把散列表該位置設為undefined 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 }
以上就是用分離鏈接法重寫的哈希表。
線性探查法第二種辦法思路更粗暴:你不是占了這個位置嘛,那我就占下一個,下個位置還被占了?那就占再下一個~
具體的實現我就不貼出來了,讀者可以自行思考并實現,然后對照代碼看看。
這里先把源代碼放出來了
創(chuàng)建更好的散列函數哈希表的實現-源代碼
以上是哈希表的兩個沖突解決辦法,但實際上應用哈希表的時候能避免沖突就盡量避免沖突,一開始的散列函數不是一個好的函數,因為沖突太多了,這里就貼書上的一個不錯的散列函數(社區(qū)),原理大致是:相加所得的hash數要夠大,且盡量為質數,用hash與另一個大于散列表大小的質數做求余,這樣得到的地址也能盡量不重復。
var djb2HashCode = function (key) { var hash = 5381 for (var i = 0; i < key.length; i++) { hash = hash * 33 + key.charCodeAt(i) } return hash % 1013 }小結
實現了字典和哈希表感覺沒有想象中那么困難,當然這還是開始。
不過,這幾天自己不斷地研究數據結構,也讓自己對于JS的理解進一步加深了。繼續(xù)努力吧~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/87227.html
摘要:我經常在業(yè)務代碼中把數據處理成這種字典的數據結構獲取的方法哈希表在學習了類之后,我們會學習散列表,也就是哈希表。 《Javascript數據結構和算法》筆記-「字典和散列表」 集合、字典、散列表存儲的都是「不重復」的數據結構 集合:我們更關注每一個元素的值,并把其作為主要元素 字典:我們用[鍵,值]的形式來存儲數據 散列表: 跟字典類似,也會是用[鍵,值]的形式來存儲數據 但是「字...
摘要:在字典中,存儲的是鍵,值,集合可以看作值,值的形式存儲元素,字典也稱為映射方法描述備注向字典中添加新元素通過某個鍵值從字典中移除對應的數據值判斷某個鍵值是存在于這個字典中通過鍵值獲取對應的數據值返回字典所有元素的數量刪除字典中所有元素將字典 在字典中,存儲的是[鍵,值],集合可以看作[值,值]的形式存儲元素,字典也稱為映射 方法 描述 備注 set(key,...
摘要:什么是散列表和散列函數哈希表,也叫散列表,是根據關鍵碼值而直接進行訪問的數據結構。根據鍵值從散列表中移除值。請實現散列表將和存在一個對象中即可定義一個包含和屬性的類并分配到散列表。 showImg(https://segmentfault.com/img/remote/1460000019005270); 這是第五周的練習題,上周忘記發(fā)啦,這周是復習 Dictionary 和 Hash...
摘要:什么是散列表和散列函數哈希表,也叫散列表,是根據關鍵碼值而直接進行訪問的數據結構。將字典的所有鍵名以數組的形式返回。根據鍵值從散列表中移除值。這是第五周的練習題,上周忘記發(fā)啦,這周是復習 Dictionary 和 HashTable。 下面是之前分享的鏈接: 1.每周一練 之 數據結構與算法(Stack) 2.每周一練 之 數據結構與算法(LinkedList) 3.每周一練 之 數據結構...
閱讀 1102·2021-11-15 18:00
閱讀 2816·2021-09-22 15:18
閱讀 1979·2021-09-04 16:45
閱讀 762·2019-08-30 15:55
閱讀 3872·2019-08-30 13:10
閱讀 1346·2019-08-30 11:06
閱讀 1994·2019-08-29 12:51
閱讀 2303·2019-08-26 13:55