Map集合、散列表

Map集合、散列表

一、Map

1.1、爲什麼需要map

前面我們學習的Collection叫做集合,它可以快速查找現有的元素。而Map在《Core Java》中稱之爲–>映射,映射的模型圖是這樣的:

mark

那爲什麼我們需要這種數據存儲結構呢???舉個例子

  • 作爲學生來說,我們是根據學號來區分不同的學生。只要我們知道學號,就可以獲取對應的學生信息。這就是Map映射的作用!

生活中還有很多這樣的例子:只要你掏出身份證(key),那就可以證明是你自己(value)

1.2、Map與Collection的區別

Map集合的 特點:將鍵映射到值的對象 ,一個映射不能包含重複的鍵,一個鍵最多隻能映射一個值(注意最後的一句話:一個鍵最多隻能映射一個值,說明就是一個鍵能映射一個值或者是0個值)

  • Map:集合中存儲的元素是成對出現的,map的鍵是唯一的,值是可以重複的。
  • collection中存儲的元素是單獨出現的,Collection的兒子set是唯一的,List是可以重複的

要點:map集合的數據結構針對鍵有效,跟值無關

​ Collection集合的數據結構針對元素有效

1.3、Map的大家庭

mark

mark

1.4、簡單的介紹一下Map的常用的功能

mark

二、散列表介紹

無論是Set還是Map,我們會發現都會有對應的–>**Hash**Set,**Hash**Map

首先我們也先得回顧一下數據和鏈表

  • 鏈表和數組都可以按照人們的意願來排列元素的次序,他們可以說是有序的(存儲的順序和取出的順序是一致的)
  • 但同時,這會帶來缺點:想要獲取某個元素,就要訪問所有的元素,直到找到爲止。
  • 這會讓我們消耗很多的時間在裏邊,遍歷訪問元素~

而還有另外的一些存儲結構:不在意元素的順序,能夠快速的查找元素的數據

  • 其中就有一種非常常見的:散列表

2.1、散列表工作原理

我們先看看hash是什麼?下面是一段摘自維基百科的解釋

散列(hashing)是電腦科學中一種對資料的處理方法,通過某種特定的函數/算法(稱爲散列函數/算法)將要檢索的項與用來檢索的索引(稱爲散列,或者散列值)關聯起來,生成一種便於搜索的數據結構(稱爲散列表)。也譯爲散列。舊譯哈希(誤以爲是人名而採用了音譯)。它也常用作一種資訊安全的實作方法,由一串資料中經過散列算法(Hashing algorithms)計算出來的資料指紋(data fingerprint),經常用來識別檔案與資料是否有被竄改,以保證檔案與資料確實是由原創者所提供。 —-Wikipedia

2.2、哈希函數

所有的哈希函數都具有如下一個基本特性:

如果兩個散列值是不相同的(根據同一函數),那麼這兩個散列值的原始輸入也是不相同的。這個特性是散列函數具有確定性的結果,具有這種性質的散列函數稱爲單向散列函數。

2.3、hash衝突

在理想的情況下,每一個關鍵字,通過散列函數(哈希函數)計算出來的地址都是一樣的,可現實中,這只是一個理想的情況。我們時常會碰到兩個關鍵字key1不等於key2,但是卻有f(key1)=f(key2),這種現象稱爲衝突(collision),並把key1和key2稱爲這個散列函數的同義詞;

綜上所述,根據散列函數f(k)和處理衝突的方法將一組關鍵字映射到一個有限的連續的地址集(區間)上,並以關鍵字在地址集中的“像”作爲記錄在表中的存儲位置,這種表便稱爲散列表,這一映射過程稱爲散列造表或散列,所得的存儲位置稱散列地址。

​ 若對於關鍵字集合中的任一個關鍵字,經散列函數映象到地址集合中任何一個地址的概率是相等的,則稱此類散列函數爲均勻散列函數(Uniform Hash function),這就是使關鍵字經過散列函數得到一個“隨機的地址”,從而減少衝突。

2.4、哈希函數構造方法

上面我們已經引出了並解釋了Hash函數 。實際工作中,需要視不同的情況採用不同的Hash函數 ,通常要考慮的因素有:

  • Hash函數 執行的時間;
  • 關鍵字 的長度;
  • Hash表 的大小;
  • 關鍵字 的分佈情況;
  • 記錄 的查找頻率;

1、直接尋址法:

取關鍵字的某個線性函數值爲Hash地址f(key)=a*key+b(a,b常數) 。

特點:由於直接地址法相當於有多少個關鍵字就必須有多少個相應地址去對應,所以不會產生衝突,也正因爲此,所以實際中很少使用這種構造方法。

2、數字分析法:

首先分析待存的一組關鍵字 ,比如是一個班級學生的出生年月日 ,我們發現他們的出生年 大體相同,那麼我們肯定不能用他們的年 來作爲存儲地址 ,這樣出現衝突 的機率很大;但是,我們發現月日 的具體數字差別很大,如果我們用月日 來作爲Hash地址 ,則會明顯降低衝突機率。因此,數字分析法就是找出關鍵字 的規律,儘可能用差異數據來構造Hash地址 ;

特點:需要提前知道所有可能的關鍵字的分佈,才能分析運用此種方法,所以不太常用。

3、平方取中法:

先求出關鍵字的平方值,然後按需要取平方值的中間幾位作爲哈希地址。這是因爲:平方後中間幾位和關鍵字中每一位都相關,故不同關鍵字會以較高的概率產生不同的哈希地址。

例:我們把英文字母在字母表中的位置序號作爲該英文字母的內部編碼。例如K的內部編碼爲11,E的內部編碼爲05,Y的內部編碼爲25,A的內部編碼爲01, B的內部編碼爲02。由此組成關鍵字“KEYA”的內部代碼爲11052501,同理我們可以得到關鍵字“KYAB”、“AKEY”、“BKEY”的內部編碼。之後對關鍵字進行平方運算後,取出第7到第9位作爲該關鍵字哈希地址,如下圖所示:

關鍵字 內部編碼 內部編碼的平方值 H(k)關鍵字的哈希地址
KEYA 11050201 122157778355001 778
KYAB 11250102 126564795010404 795
AKEY 01110525 001233265775625 265
BKEY 02110525 004454315775625 315

特點:適合不知道關鍵字的分佈,而且位數 又不是很大的情況

4、摺疊法:

將關鍵字分割成位數相同的幾部分(最後一部分位數可以不同),然後取這幾部分的疊加和(去除進位)作爲散列地址。數位疊加可以有移位疊加和間界疊加兩種方法。移位疊加是將分割後的每一部分的最低位對齊,然後相加;間界疊加是從一端向另一端沿分割界來回摺疊,然後對齊相加。

特點是:事先不知道關鍵字的分佈,適合關鍵字位數較多的情況;

除留取餘法:

5、除留取餘法:

f(key)=key mod p (p<=m);

取關鍵字被某個不大於Hash表 長m 的數p 除後所得的餘數爲Hash地址 。

特點:這是最簡單也是最常用的Hash函數構造方法。可以直接取模,也可以在平法法、摺疊法之後再取模。

值得注意的是,在使用除留取餘法 時,對p 的選擇很重要,如果p 選的不好會容易產生同義詞 。由經驗得知:p 最好選擇不大於表長m的一個質數(素數 ) 、或者不包含小於20的質因數的合數。

6、隨機數法:

f(key)=Random(key)

選擇一個隨機函數,取關鍵字的隨機函數值作爲Hash地址 ,通常用於關鍵字長度不同的場合。即

特點:通常,關鍵字長度不相等時,採用此法構建Hash函數 較爲合適。

2.5、處理衝突

如何處理衝突是哈希造表不可缺少的一個方面。現在完整的描述一下處理衝突:

假設哈希表的地址集爲,衝突是指由關鍵字得到的哈希地址爲的位置上已存有記錄,則“處理衝突”就是爲該關鍵字的記錄找到另一個“空”的哈希地址。  在處理衝突的過程中可能得到一個地址序列。即在處理哈希地址的衝突時,若得到的另一個哈希地址仍然發生衝突,則再求下一個地址,若仍然衝突,再求,依次類推,直至不發生衝突爲止,則爲記錄在表中的地址。  (需要注意此定義不太適合鏈地址法)

1、開放定址法:

所謂的開放地址法就是一旦發生了衝突,就去尋找下一個空的散列地址,只要散列表足夠大 ,空的散列位置總能找到的,並將記錄存入:

f(key)=(f(key)+di) MOD m(i=1,2,3,4,5,6…….m-1);爲哈希函數; 爲哈希表表長;
爲增量序列,有3種取法:

  1. 稱爲線性探測再散列;

    fi(key)=(f(key)+di) MOD m(i=1,2,3,4,5,6…….m-1);

  2. 稱爲二次探測再散列;

    fi(key)=(f(key)+di) MOD m (di=12,−12,22,−22⋯⋯,q2,−q2,q≤m−1)

  3. 稱爲僞隨機探測再散列;

    fi(key)=(f(key)+di) MOD m(di是隨機數列)

2、再散列函數法:

​ 用多個不同的散列函數

3、鏈地址法:

將所有關鍵字爲同義詞的記錄存儲在同一線性表中。即在Hash 出來的哈希地址中不直接存Key ,而是存儲一個Key 的鏈表 ,當發生衝突 時,將同義的Key 加入鏈表 ;

img

4、公共溢出區:

可以建立一個公共溢出區,用來存放有衝突的Key 。比如設立另一個哈希表,專門用來存放出現衝突的同義詞。

2.6、查找及分析

在哈希表上進行查找的過程和哈希造表的過程基本是一致的,過程就不累述了。我們需要看一看其查找的長度。

  1. 平均查找長度

    1. 雖然哈希表在關鍵字與記錄的存儲位置之間建立了直接映像,但由於“衝突”的存在,使得哈希表的查找過程仍然是一個“給定值和關鍵字進行比較”的過程。因此,仍需以平均查找長度作爲衡量哈希表的查找效率的量度;

      (還記得上面我們說的“理想情況下”嗎?~~ 現實告訴我們,一般情況下,還是不得不需要“比較”!)

    2. 查找過程中需要和給定值進行比較的關鍵字的個數取決於下列三個因素:

      • 哈希函數;
      • 處理衝突的方法;
      • 哈希表的裝填因子;
  2. 裝填因子

    在一般情況下,我們設計的哈希函數肯定是儘量均勻的,所以可以不考慮它對平均查找長度的影響。那麼,處理衝突方法相同的哈希表,其平均查找長度就依賴於哈希表的裝填因子了。其定義如下:裝填因子標誌哈希表的裝滿程度

    裝填因子=表中的記錄數/哈希表的長度

    直觀的看:

    • 越小,表明表中還有很多的空單元 ,發生衝突的可能性就越小;
    • 越大,代表着表中已填入的元素越多,再填入元素時發生衝突的可能性就越大。那麼在查找時,給定值需要比較的關鍵字的個數就越多;

    因此,Hash表的平均查找長度和裝填因子有關。有相關文獻證明當裝填因子在0.5左右的時候,Hash的性能能夠達到最優。因此,一般情況下,裝填因子取經驗值0.5。

Hash表的平均查找時間包括查找成功時的平均查找長度和查找失敗時的平均查找長度

查找成功時的ASL=表中每個元素查找成功時的比較次數之和/表中元素個數;

查找不成功時的ASL=在表中查找元素不成功時的平均比較次數,可以理解爲向表中插入某個元素,該元素在每個位置都有可能,然後計算出在每個位置能夠插入時需要比較的次數,再除以表長即爲查找不成功時的平均查找長度。

ASL指的是 平均查找時間

下面舉個例子:

關鍵字序列:(7、8、30、11、18、9、14)

散列函數:  H(Key) = (key x 3) MOD 7

裝載因子:  0.7

處理衝突:線性探測再散列法

查找成功的ASL計算方法:

因爲現在的數據是7個,填充因子是0.7。所以數組大小=7/0.7=10,即寫出來的散列表大小爲10,下標從0~9。
第一個元素7,帶入散列函數,計算得0。
第二個元素8,帶入散列函數,計算得3。
第三個元素30,帶入散列函數,計算得6。
第四個元素11,帶入散列函數,計算得5。
第五個元素18,帶入散列函數,計算得5;此時和11衝突,使用線性探測法,得7。
第六個元素9,帶入散列函數,計算得6;此時和30衝突,使用線性探測法,得8。
第七個元素14,帶入散列函數,計算得0;此時和7衝突,使用線性探測法,得1。
所以散列表

地址 0 1 2 3 4 5 6 7 8 9
key 7 14 8 11 30 18 9

所以查找成功的計算:
如果查找7,則需要查找1次。
如果查找8,則需要查找1次。
如果查找30,則需要查找1次。
如果查找11,則需要查找1次。
如果查找18,則需要查找3次:第一次查找地址5,第二次查找地址6,第三次查找地址7,查找成功。
如果查找9,則需要查找3次:第一次查找地址6,第二次查找地址7,第三次查找地址8,查找成功。
如果查找地址14,則需要查找2次:第一次查找地址0,第二次查找地址1,查找成功。
所以,ASL=(1+2+1+1+1+3+3)/ 7=12/ 7

查找不成功的ASL計算方法:

1. 定義什麼叫查找不成功  舉個例子來說吧。在已知上面散列表的基礎上,如果要查找key爲4的關鍵字。根據散列函數可以計算Hash(key)=Hash(4)=5。此時在地址爲5的地方取出那個數字,發現key=11,不等於4。這就說明在裝填的時候會發生衝突。根據衝突處理方法,會繼續檢測地址爲6的值,發現key=30,依然不等。這個時候到了地址爲6,但是依然沒有找到。那麼就說明根本就沒有key=4這個關鍵字,說明本次查找不成功。注意:爲什麼到地址6?因爲散列函數中有 mod7 ,對應的地址爲0-6,即0~6查找失敗的查找次數。  再舉一個例子。查找key爲0的關鍵字,根據散列函數可以計算Hash(key)=Hash(0)=0。此時在地址爲0的地方取出那個數字,發現key=7,不等於0。這就說明在裝填的時候會發生衝突。根據衝突處理方法,會繼續檢測地址爲1的值,發現key=14,依然不等。這個時候到了地址爲3,發現爲空依然沒有找到。所以停止查找,本次查找不成功。因爲如果key=0這個關鍵字存在的話,依照衝突處理函數,就一定能找到它。總不能丟了吧。

查找地址爲0的值所需要的次數爲3,

  查找地址爲1的值所需要的次數爲2,

  查找地址爲2的值所需要的次數爲1,

  查找地址爲3的值所需要的次數爲2,

  查找地址爲4的值所需要的次數爲1,

  查找地址爲5的值所需要的次數爲5,

  查找地址爲6的值所需要的次數爲4。

3.計算  查找不成功ASL=(3+2+1+2+1+5+4)/ 7=18/ 7

參考文獻

Hash表的平均查找長度ASL計算方法

java中哈希表及其應用詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章