【算法】散列表
散列表
散列函數
定義
散列函數“將輸入映射到數字”。即無論你給它什麽數據,它都還你一個數字。
散列函數必須滿足一些要求
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 散列表非常適合用於防止重復。
【算法】散列表