爲何散列表會出現
- 場景:若是超市買東西結帳的時候,售貨員在一個本子裏查找價格
- 若是是有序的(二分查找)還好,時間是 O(logn),若是是無序(簡單查找)的話,那就是 O(n)
- 若是直接有一個 O(1)的查找速度就行了
散列函數
將輸入映射到數字數組
- 一致性:輸入 apple 時獲得的是 4,那麼每次輸入 apple 都必須是 4
- 不一樣性:不一樣的輸入將獲得不一樣的輸出。若是輸入 apple 和 yoki 都獲得 4,那麼這是一個很差的散列函數。
創造散列表
結合散列函數和數組瀏覽器
- 建立一個空數組
- apple 輸入到散列函數,輸出 3,而後把 apple 的價格存儲到數組索引 3
- 接着各個類推,知道存完數組
- 而後咱們要找 apple 的價格,就把 apple 輸入交給散列函數,獲得 3 去數組裏面找
散列表的經典應用
查找,防止重複,用於緩存緩存
查找
- 被用於大海撈針的查找
- 這個就不用細說了,相似的有 dns 解析
防止重複
場景:負責一個投票站,每一個人只能投一票,如何避免重複投票服務器
- 有一個方法是:有人來投票,就將它記錄在一個投票名單裏面,而後接下來的人都遍歷這個投票名單,若是有就不能投票,可是這樣就列表會愈來愈長,就會變慢
- 另一種就是散列表,超快
緩存
- 瀏覽器緩存的數據存儲在散列表裏面
- 若是訪問 facebook 的頁面,會先檢查散列表中是否存儲了該頁面
- 若是不在緩存中,纔去訪問服務器,而後把數據放到緩存裏,這樣下次有人訪問就能夠直接命中緩存
衝突
事實上不可能不一樣的輸入都能得到不一樣的值app
- 假設有一個數組包含 26 個位置,而咱們使用的散列函數很是簡單,按照字母表順序分配位置
- apple 第一個,bear 第二個,接下來來了一個 banana,理應分到第二個,可是這個時候第二個位置已是 bear 了
- 這個就叫衝突,解決衝突的辦法有不少,簡單的就是在這個衝突的位置存儲一個鏈表,bear 末尾的指針指向 banana
- 因此散列函數很重要,好的散列函數將 key 均勻的映射到散列表的不一樣位置
如何避免衝突
- 較低的填裝因子
- 散列表包含的元素數:位置總數就是填裝因子
- 越小越好,滿了只能調整長度,換一個更長的數組
- 一個不錯的經驗規則就是,一旦填裝因子大於 0.7,那麼就要調整散列表的長度
- 良好的散列函數
散列表性能如何
平均狀況
最糟糕狀況
爲何會有兩種狀況,這是由於有了衝突可能會靠鏈表解決,n 個長度的鏈表,最好狀況下沒有衝突,就常量時間 )函數