【算法】算法圖解筆記_散列表

線性查找的時間複雜度O(n),二分查找爲O(logn),有沒有時間複雜度爲O(1)的查找嗎? 固然有,這就是散列表。python

散列函數

散列函數「將輸入映射到數字」。其必須知足一些要求。數組

  1. 它必須是一致的。對於一樣的輸入,輸出必須是同樣的。
  2. 最理想的狀況是,將不一樣的輸入映射到不一樣的數字。這樣,不一樣的輸入就會映射到不一樣的位置。

這樣就能夠使用散列函數將輸入映射到數組的不一樣位置,從而獲得一個簡單的散列表(hash table)。散列表是目前該書介紹的第一種包含額外邏輯的數據結構。數組和鏈表都被直接映射到內存,但散列表更復雜,它使用散列函數來肯定元素的存儲位置。散列表由組成,將鍵映射到值。緩存

同時散列表也被稱爲散列映射映射字典關聯數組。好比,Python提供的散列表實現爲字典。Python內置字典的用法:服務器

>>> book = dict() # 建立空字典
>>> book["apple"] = 0.67    # 添加鍵值對
>>> book["milk"] = 1.49        # 添加鍵值對
>>> book["avocado"] = 1.49    # 添加鍵值對
>>> print(book)            # 打印當前散列表
{'apple': 0.67, 'milk': 1.49, 'avocado': 1.49}
>>> print(book["avocado"])    # 散列表使用鍵查找值
1.49

應用案例

將散列表用於查找

經過鍵快速查找其關聯的值。數據結構

好比,電話簿
姓名爲鍵,電話號碼爲值app

DNS解析(DNS resolution)
域名爲鍵,IP地址爲值函數

防止重複

好比,投票限制每人只能投一票。能夠將某人的信息(如,姓名、IP等)做爲鍵存到散列表內,每次用戶投票前,先看看以前有沒有投過,沒投的話容許投票,並將信息存到散列表內,若是投過票,則不容許再投。性能

將散列表用做緩存

緩存是一種經常使用的加速方式,全部大型網站都使用緩存,而緩存的數據則存儲在散列表中。緩存的工做原理:網站將數據記住,而再也不從新計算,這樣的好處是減小響應的時間,同時也節省了服務器的計算資源。網站

當訪問網站的頁面時,它首先檢查散列表中是否存儲了該頁面。僅當URL不在緩存中時,你才讓服務器作些處理,並將處理生成的數據存儲到緩存中,再返回它。這樣,當下次有人請求該URL時,你就能夠直接發送緩存中的數據,而不用再讓服務器進行處理了。spa

衝突(collision)

最理想的狀況是,散列函數將不一樣的輸入映射到不一樣的數字,但幾乎不可能編寫出這樣的散列函數。
衝突:給兩個鍵分配的位置相同。

處理衝突的方式不少,最簡單的辦法以下:若是兩個鍵映射到了同一個位置,就在這個位置存儲一個鏈表。
圖片描述

這種處理方式也有可能出現最糟的狀況,如,除第一個位置外,整個散列表都是空的,而第一個位置包含一個很長的列表,這時查找速度等同於鏈表的查找速度,會很慢。

好的散列函數不多致使衝突,這樣會將鍵均勻地映射包散列表的不一樣位置,從而使鏈表不會太長。

性能

在平均狀況下,散列表執行各類操做的時間都爲O(1)。O(1)被稱爲常量時間。常量時間並不意味着立刻,而是說無論散列表多大,所需的時間都相同。在最糟狀況下,散列表全部操做的運行時間都爲O(n)——線性時間。

要避免衝突,須要有:

  • 較低的填裝因子;

填裝因子 = 散列表包含的元素數 / 位置總數
填裝因子度量的是散列表中有多少位置是空的。

一旦填裝因子開始增大,你就須要在散列表中添加位置,這被稱爲調整長度(resizing) 。調整長度須要從新建立新的存儲空間,而後使用散列函數將全部的元素都插入到新的散列表內,因此開銷較大。但平均而言,即使考慮到調整長度所需的時間,散列表操做所需的時間也爲O(1)。

填裝因子越低,發生衝突的可能性越小,散列表的性能越高。一個不錯的經驗規則是:一旦填裝因子大於0.7,就調整散列表的長度。

  • 良好的散列函數。

良好的散列函數讓數組中的值呈均勻分佈。糟糕的散列函數讓值扎堆,致使大量的衝突。

請繼續關注個人公衆號文章
圖片描述

相關文章
相關標籤/搜索