hash 哈希查找復雜度為什么這么低?


hash 哈希查找復雜度為什么這么低?

(2017-06-23 21:20:36)
  分類: c
from:
作者:jillzhang
出處:http://jillzhang.cnblogs.com/ 
 
還有:http://blog.csdn.net/wendavidoi/article/details/50670016
http://www.cnblogs.com/dolphin0520/archive/2012/09/28/2700000.html
哈希算法,又稱散列算法,能大大提高搜索的效率。它的主要工作是將一個數字映射到一個表格的某個地方打一個比喻,哈希就像那些公司前台的接待人員,直接將領導的電話記住。而哈希,就是將每一個元素的位置記住,就是我們不去找某個東西,而是將它的位置算出來。
 

1)hash它為什么對於鍵-值查找性能高
學過數據結構的,都應該曉得,線性表和樹中,記錄在結構中的相對位置是隨機的,記錄和關鍵字之間不存在明確的關系,因此在查找記錄的時候,需要進行一系列的關鍵字比較,這種查找方式建立在比較的基礎之上,在.net中(Array,ArrayList,List)這些集合結構采用了上面的存儲方式。
比如,現在我們有一個班同學的數據,包括姓名,性別,年齡,學號等。假如數據有

姓名 性別 年齡 學號
張三 15 1
李四 14 2
王五 14 3

 

假如,我們按照姓名來查找,假設查找函數FindByName(string name);
1)查找“張三”
只需在第一行匹配一次。
2)查找"王五"
   在第一行匹配,失敗,
   在第二行匹配,失敗,
   在第三行匹配,成功
上面兩種情況,分別分析了最好的情況,和最壞的情況,那么平均查找次數應該為 (1+3)/2=2次,即平均查找次數為(記錄總數+1)的1/2。
盡管有一些優化的算法,可以使查找排序效率增高,但是復雜度會保持在log2n的范圍之內。
如何更更快的進行查找呢?我們所期望的效果是一下子就定位到要找記錄的位置之上,這時候時間復雜度為1,查找最快。如果我們事先為每條記錄編一個序號,然后讓他們按號入位,我們又知道按照什么規則對這些記錄進行編號的話,如果我們再次查找某個記錄的時候,只需要先通過規則計算出該記錄的編號,然后根據編號,在記錄的線性隊列中,就可以輕易的找到記錄了 。
注意,上述的描述包含了兩個概念,一個是用於對學生進行編號的規則,在數據結構中,稱之為哈希函數,另外一個是按照規則為學生排列的順序結構,稱之為哈希表。
仍以上面的學生為例,假設學號就是規則,老師手上有一個規則表,在排座位的時候也按照這個規則來排序,查找李四,首先該教師會根據規則判斷出,李四的編號為2,就是在座位中的2號位置,直接走過去,“李四,哈哈,你小子,就是在這!”
看看大體流程:
  
從上面的圖中,可以看出哈希表可以描述為兩個筒子,一個筒子用來裝記錄的位置編號,另外一個筒子用來裝記錄,另外存在一套規則,用來表述記錄與編號之間的聯系。這個規則通常是如何制定的呢?

 
 
直接取值法
直接取值法,就是直接以當前元素的值來決定它的位置。化成函數就是 H(x)=x。這種方法的好處是不可能沖突,除非兩個元素一模一樣。而且這樣甚至能夠保證在哈希表里面的元素有序,就像計數排序一樣。
但是這種方法也有缺點,當x的取值太大的時候,耗費的空間同時也會很大。舉個例子,如果有3個數:3,6814246421,1654654614874213,那光是這三個數,就已經耗費了巨大的內存空間了。
除法哈希
既然直接取值會耗費很大的內存空間,那我們可以模一下這個變量,一般來說,模一個數組長度,就是不錯的選擇。這樣既可以剛剛好放下這些數據,又不會耗費太多的空間。化成函數就是H(x)=x%m。但是這樣就會出現沖突。所謂沖突,就是指兩個取值不一樣的數,它們在哈希后得出的值相同,映射到了同一個位置。也就是說,a!=b,但H(a)==H(b)。在這里我們先不討論沖突。那怎樣盡量避免沖突呢?答案就是:模一個素數!可以證明,當H(x)定義中x%m的m的因數越多,則沖突的概率就越大。不過,其實最好的方法還是增大表格的大小,這樣相應的,x%m的取值也會更為多樣化。
位運算哈希
除法哈希的缺點之一,是容易沖突,而且有的時候甚至還不與整一個數相關。下面我就介紹一種位運算哈希,這種哈希主要運用乘法,而且多是位運算,速度較快。同時,除法哈希要求數組長度最好是一個素數,但在計算機中,我們更喜歡讓數組長度為2的冪數,這樣就不會浪費空間。確切地說,就是利用位運算,充分的混合元素。舉個例子,ELFHash就是一個很好的實現。
 
乘法哈希
最后介紹一種最實用且最容易記的哈希算法。這種哈希函數叫做乘法哈希。其原理就是將原數看做一個n進制的數在轉換回十進制。這種哈希算法的典型實現有BKDRHash。理解起來很容易,也是奧賽中經常用到的算法,一般來說沖突率非常小。
 
順帶附上BKDRHash的核心代碼(已過測試):

unsigned int BKDRHash(char *key){
unsigned int seed=131;
unsigned int hash=0;

while(*key)
{
hash = hash * seed + (*key++);
}
return hash%MOD;
}
乘法哈希較常用到
hash <wbr>哈希查找復雜度為什么這么低?
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM