pymongo查询列表元素_散列表:如何实现word编辑器的拼写检查?
Word文檔編輯器大家應(yīng)該經(jīng)常使用吧,大家有沒有留意到它編輯功能,當(dāng)我們輸入一個錯誤的單詞時,單詞單面就會標(biāo)紅提示“拼寫錯誤”,這個功能是怎么實(shí)現(xiàn)的呢?其實(shí)啊,它是通過散列表實(shí)現(xiàn)的,學(xué)習(xí)了散列表原理后你就懂得這個功能的實(shí)現(xiàn)方式了。
散列表
散列表的英文名叫Hash Table,一般叫散列表或哈希表,散列表用的是數(shù)組支持按照下標(biāo)隨機(jī)訪問數(shù)據(jù)的特性,所以散列表就是數(shù)組的一種擴(kuò)展,由數(shù)組演化而來,可以說,如果沒有數(shù)組就沒有散列表。
我用一個列子解釋一下,我們?nèi)ビ斡攫^游泳時一般都會寄存衣物,這時前臺就會登記我們名字后分配一個儲物柜編號卡,后面我們通過這個編號卡就能快速地找到柜子存儲衣物,回去時也能快速找到柜子取回衣物。
這里儲物柜是按照編號順序排列,就相當(dāng)于一個數(shù)組,由于每天去游泳的人都各不相同,就不能每個柜子都貼上對應(yīng)人的名字了,所以儲物前就會先去前臺分配一個編號,再根據(jù)編號的下標(biāo)存儲在數(shù)組的下標(biāo)位置。
這就是典型的散列思想。每個去游泳的人的名字我們叫做鍵(key)或者關(guān)鍵字。我們把前臺通過名字分配儲物柜號的對應(yīng)過程叫作散列函數(shù),而通過散列函數(shù)計(jì)算得到的儲物柜號碼叫作散列值。
散列函數(shù)
散列函數(shù),顧名思義,它就是一個函數(shù),我們可以把它定義為hash(key),其中key就是元素的鍵,hash(key)就是通過散列函數(shù)計(jì)算得到的散列值。
剛剛舉的例子中,散列函數(shù)其實(shí)就是前臺工作人員將名字和號碼牌對應(yīng)起來的一個對應(yīng)關(guān)系,這個例子比較不恰當(dāng),并沒有一個固定的公式。那么,實(shí)用場景中,要怎么設(shè)計(jì)構(gòu)造散列函數(shù)呢,我總結(jié)了三點(diǎn)基本的要求:
第一點(diǎn)很容易理解,散列值最后是作為數(shù)組的下標(biāo)的,數(shù)組下標(biāo)是從0開始的;第二點(diǎn),相同的key,得到的散列值也應(yīng)該是相同的。
第三點(diǎn)看起來合情合理,但是在真實(shí)場景中,要想找到一個不同的鍵得出的散列值都不一樣的散列函數(shù)幾乎是不可能的,即便像業(yè)界著名的MD5、SHA、CRC等哈希算法也無法避免散列沖突,因?yàn)閿?shù)組的空間有限,函數(shù)計(jì)算得到的值還必須在數(shù)組個數(shù)范圍內(nèi),因此就會有很大概率出現(xiàn)沖突。
散列沖突
再好的散列函數(shù)也無法避免散列沖突,那怎么辦呢?只能通過其他方式解決,一般散列沖突的解決辦法有兩類:開放尋址法和鏈表法。
1.開放尋址法
開放尋址法的核心思想是,如果出現(xiàn)了散列沖突,就尋找下一個空閑位置,插入新的數(shù)據(jù)。開放尋址法也有多種方式,將介紹一個簡單的探測方法,線性探測(Linear Probing)。
線性探測
當(dāng)我們往散列表插入數(shù)據(jù)時,如果經(jīng)過散列函數(shù)散列之后,存儲位置已經(jīng)被占用了,我們就從當(dāng)前位置依次往后查找,將數(shù)據(jù)插入到找到的空閑位置,如果遍歷到尾部仍沒有空閑位置,我們就從表頭開始找,直到找到為止。如圖所示
通過線性探測要查找數(shù)據(jù)時,和插入數(shù)據(jù)類似,也是通過散列函數(shù)得到對應(yīng)位置的元素,和要查詢的數(shù)據(jù)作對比,如果一致,則取出該值,如果不一致,則從該位置往下到散列表的空閑位置一個個查找,如果找到,取出對應(yīng)值,如果沒有找到,則數(shù)據(jù)不存在。
而但通過線性探測法刪除一個元素時就比較麻煩,如果查找到對應(yīng)的元素時直接將該元素對應(yīng)的位置置空的話,那按照上面說的線性探測查找方法,遇到一個空的位置時就停止查詢,那這個空位置如果是剛剛被刪除的元素,這時候這個查找方法就失敗了。所以,刪除一個元素時并不是直接刪除,而是在要刪除的位置標(biāo)記deleted。在查找一個元素時如果遇到deleted標(biāo)記的元素,則繼續(xù)往下查找,如下圖所示。
通過上面的介紹,我們可以知道,線性探測法有一個弊端。就是散列表剩余空間不足時,就會頻繁地出現(xiàn)散列沖突,導(dǎo)致效率不高,極端情況下插入一個元素會時間復(fù)雜度為O(n)。
對于開放尋址的沖突解決方法,除了線性探測方法外,還有另外兩種比較經(jīng)典的探測方法,分別為二次探測和雙重散列。
二次探測
二次探測法,和線性探測法類似,線性探測每次的探測步長是1步,它探測的下標(biāo)序列是hash(key)+0,hash(key)+1,hash(key)+2,hash(key)+3...而二次探測的步長是原來的“二次方步長”,它的探測下標(biāo)序列是列是hash(key)+0,hash(key)+1,hash(key)+4,hash(key)+9...
雙重散列
雙重散列,意思是不僅要使用一個散列函數(shù),我們要使用一組散列函數(shù)hash1(key),hash2(key),hash3(key)...先使用第一個散列函數(shù)計(jì)算散列位置,如果出現(xiàn)沖突,再使用第二個散列函數(shù),依次類推,直到找到空閑位置。
不管使用哪種線性沖突解決方法,當(dāng)空閑位置較少的時候,出現(xiàn)沖突的概率就會加大,為了保證散列表的操作效率,一般會保證散列表有一定比例的空閑位置,我們用裝載因子來表示散列表的空閑比例,它的計(jì)算公式如下
散列表的裝載因子 = 填入表中的元素個數(shù) / 散列表長度2.鏈表法
鏈表法是一種更加普遍的散列表沖突解決方法,相比線性探測法,它更簡單更容易理解。如圖所示,散列表的元素就是一個“桶”或“槽”,每個桶都放入一個鏈表,將散列值相同的元素都放在同一個鏈表中。
當(dāng)插入的時候,我們只需要通過散列函數(shù)計(jì)算出對應(yīng)的散列槽位,將其插入到對應(yīng)的鏈表中即可,時間復(fù)雜度為O(1)。當(dāng)要查詢或刪除時,即通過同樣的方法找到對應(yīng)的槽位,再遍歷鏈表查詢或刪除,那查詢和刪除的時間復(fù)雜度是多少呢?
查詢和刪除的時間復(fù)雜度和每個槽位鏈表的長度成正比,假設(shè)鏈表平均長度為k,那時間復(fù)雜度則為O(k)。對于散列比較均勻的散列函數(shù),理論上k = n / m,其中n為散列表中數(shù)據(jù)的個數(shù),m為散列表的長度。
解答開篇
有了上面的散列表的介紹,我們再來回顧下開篇提到的Word文檔編輯器的拼寫錯誤提示是怎么實(shí)現(xiàn)的?
我們常用的英文單詞大約有20萬個左右,假設(shè)平均每個單詞有10個字母,那每個單詞就大約有10個字節(jié),20萬個單詞就有差不多2M左右的大小,對于現(xiàn)代的計(jì)算機(jī)來說,完全可以將20萬個單詞放在內(nèi)存中,存儲在散列表,每次輸入一個單詞時,就通過散列表查找,如果能找到就是拼寫正確的,如果找不到則提示拼寫錯誤。
總結(jié)
以上是生活随笔為你收集整理的pymongo查询列表元素_散列表:如何实现word编辑器的拼写检查?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中messagebox用法实
- 下一篇: 求离散数据的突变点_Nat Gen |