1. 程式人生 > >【算法】散列表

【算法】散列表

存儲 print rom 包含 不錯 最大 true 檢查 處的

散列表

散列函數

定義

散列函數“將輸入映射到數字”。即無論你給它什麽數據,它都還你一個數字。

散列函數必須滿足一些要求

l 它必須是一致的。例如,假設你輸入apple時得到的是4,那麽每次輸入apple時,得到的都必須為4。

l 它應將不同的輸入映射到不同的數字。 例如, 如果一個散列函數不管輸入是什麽都返回1,它就不是好的散列函數。最理想的情況是,將不同的輸入映射到不同的數字。

散列函數準確地指出了存儲位置,具體原因如下:

l 散列函數總是將同樣的輸入映射到相同的索引。

l 散列函數將不同的輸入映射到不同的索引。

l 散列函數知道數組有多大,只返回有效的索引。

散列表

l 使用散列函數和數組創建了一種被稱為散列表(hashtable)的數據結構

l 散列表是一種包含額外邏輯的數據結構。數組和鏈表都被直接映射到內存,但散列表更復雜,它使用散列函數來確定元素的存儲位置

l 散列表也使用數組來存儲數據,因此其獲取元素的速度與數組一樣快

python使用字典來實現散列表功能,可使用函數dict創建散列表

應用

將散列表用於查找

示例:

l 創建映射。

l 查找。

>>> phonebook={}
>>> phonebook[‘li‘]=123456
>>> phonebook[‘p‘]=987654
>>> print(phonebook[‘p‘])
987654

防止重復

示例:檢查是否存在某個元素

voted={}
def check_voter(name):
    if voted.get(name):              #使用函數get來返回是否存在。不存在返回None
        print(‘kick them out!‘)
    else:
        voted[name]=True
        print(‘let them vote‘)
 
check_voter(‘tom‘)

check_voter(‘jerry‘)

check_voter(‘jerry‘)

將散列表用作緩存

緩存的工作原理

:網站將數據記住,而不再重新計算。

緩存優點

l 用戶能夠更快地看到網頁

l 需要做的工作更少。

緩存是一種常用的加速方式,所有大型網站都使用緩存,而緩存的數據則存儲在散列表中!

訪問過程

cache={}
def get_page(url):
    if cache.get(url):                        #檢查緩存中是否存儲了該頁面
        return cache[url]                      #存儲了,即返回它
    else:
        data=get_data_from_server(url)            #沒存儲,從服務器調用
        cache[url]=data                           #將其存儲到緩存中
        return data                               #返回該頁面

  

小結

散列表適合用於:

l 模擬映射關系;

l 防止重復;

l 緩存/記住數據,以免服務器再通過處理來生成它們

沖突(collision)

定義

給兩個鍵分配的位置相同。

處理沖突的方式

如果兩個鍵映射到了同一個位置,就在這個位置存儲一個鏈表。

經驗

l 散列函數很重要。避免散列函數將所有的鍵都映射到一個位置,而最理想的情況是,散列函數將鍵均勻地映射到散列表的不同位置。

l 如果散列表存儲的鏈表很長,散列表的速度將急劇下降。然而, 如果使用的散列函數很好,這些鏈表就不會很長!

散列函數很重要,好的散列函數很少導致沖突。

性能

在平均情況下,散列表執行各種操作的時間都為O(1) O(1)被稱為常量時間

散列表的性能

操作

平均情況

最糟情況

查找

O(1)

O(n)

插入

O(1)

O(n)

刪除

O(1)

O(n)

散列表同數組和鏈表比較

操作

平均情況

最糟情況

數組

鏈表

查找

O(1)

O(n)

O(1)

O(n)

插入

O(1)

O(n)

O(n)

O(1)

刪除

O(1)

O(n)

O(n)

O(1)

在平均情況下,散列表的查找(獲取給定索引處的值)速度與數組一樣快,而插入和刪除速度與鏈表一樣快,因此它兼具兩者的優點!

但在最糟情況下,散列表的各種操作的速度都很慢。

在使用散列表時,避開最糟情況至關重要。需要有:

l 較低的填裝因子

l 良好的散列函數。

填裝因子

計算公式

散列表的填裝因子=散列表包含的元素數/位置總數

調整長度(resizing)

填裝因子大於1意味著元素數量超過了數組的位置數。

一旦填裝因子開始增大,就需要在散列表中添加位置,這被稱為調整長度(resizing)

經驗

填裝因子越低,發生沖突的可能性越小,散列表的性能越高。

一個不錯的經驗規則是:一旦填裝因子大於0.7,就調整散列表的長度

良好的散列函數

良好的散列函數讓數組中的值呈均勻分布

糟糕的散列函數讓值紮堆,導致大量的沖突。

小結

l 可以結合散列函數和數組來創建散列表。

l 沖突很糟糕,應使用可以最大限度減少沖突的散列函數

l 散列表的查找、插入和刪除速度都非常快。

l 散列表適合用於模擬映射關系

l 一旦填裝因子超過0.7,就該調整散列表的長度

l 散列表可用於緩存數據(例如,在Web服務器上)。

l 散列表非常適合用於防止重復

【算法】散列表