1. 程式人生 > 其它 >【LeetCode】842. Split Array into Fibonacci Sequence 將陣列拆分成斐波那契序列(Medium)(JAVA)

【LeetCode】842. Split Array into Fibonacci Sequence 將陣列拆分成斐波那契序列(Medium)(JAVA)

散列表是一種非常常用的資料結構,在python中,字典與集合的底層實現都是散列表,也叫hash表。

1.雜湊函式

想要了解散列表首先需要知道雜湊函式,雜湊函式的使用非常簡單,當你傳入一個不可變型別的資料,他會返回一個固定的數字,並且他滿足如下的條件

1.雜湊函式的結果必須是一致的,例如,在你輸入apple時候得到的雜湊值5,那麼每次輸入apple得到的結果一定是5
2.對於不同的輸入,他應該儘量對映到不同的數字如果一個雜湊函式不管輸入是什麼都返回1,它就不是好的雜湊函式。我們最理想的情況,希望所有不同的輸入都有不同的輸出。

對於絕大部分的程式設計師都不需要編寫雜湊函式,因為你使用的語言一定實現了雜湊函式且效能較佳。

2.散列表

當我們使用雜湊函式之後,就可以對於一個固定的輸入得到一個固定的輸出,那麼這個輸出就可以作為索引來尋找值。我們知道,對於一個已知的索引,陣列的查詢資料的時間複雜度是O(1)。所以當我們使用雜湊函式配合陣列,就組成一個簡易版的散列表。

不斷重複以上的過程,最終我們的陣列將被填滿(理想情況下)

此時如果我們需要查詢apple的值,只要通過雜湊函式得到他的值

就可以通過陣列直接拿到他的值。

3.散列表的衝突

上面有提到,我們希望雜湊函式儘量滿足所有的輸入都得到不同的輸出,但是在實際上,很難有這種演算法作為支撐,即可能會出現兩個不同的輸入得到相同的輸出。

比如我們自定義一個雜湊函式,定義個長度為26的陣列(對應英文字母)如下:

此時我們如果有apple和arocados(鱷梨),都是存入陣列為0的位置,此時就產生了衝突,此時有幾種解決衝突的方法,第一是改變我們的雜湊函式,讓其能夠分配更對的輸出,但是注意,分配非常大的陣列對於記憶體消耗也是很大的。

第二種我們可以配合陣列和連結串列,在衝突的地方,設計連結串列,形成下面的樣子。

當然上面的設計如果雜湊函式有問題,那麼也會造成問題,比如下面的情況,我們只儲存a開頭的資料。

所以有兩點很重要:

1.雜湊函式很重要。前面的雜湊函式將所有的鍵都對映到一個位置,而最理想的情況是,雜湊函式將鍵均勻地對映到散列表的不同位置。

2.如果散列表儲存的連結串列很長,散列表的速度將急劇下降。然而,如果使用的雜湊函式很好,這些連結串列就不會很長!

4.雜湊函式效能

在平均情況下,散列表執行各種操作的時間都為O(1)。O(1)被稱為常量時間。他的意思不是馬上返回資料,而是不管多大的散列表,所需的時間都一樣。我們知道,遍歷查詢的時間複雜度是O(n)。二分查詢速度快一些,達到了O(logn)。所以當我們使用for迴圈在兩個都有一千萬資料的字典和列表中查詢資料的時候,速度差別是很多的。當資料量越來越大,列表查詢會越慢,而字典的查詢速度幾乎沒有變化。

在最糟的情況下,散列表所有的操作時間也都為O(n),這和你使用的雜湊函式有很大關係

填裝因子:代表了這個散列表中的剩餘空白情況

5.總結

1.我們可以結合雜湊函式和陣列來建立散列表
2.衝突很糟糕,你應使用可以最大限度減少衝突的雜湊函式。
3.散列表的查詢、插入和刪除速度都非常快。
4.散列表適合用於模擬對映關係。
5.一旦填裝因子超過0.7,就該調整散列表的長度。
6.散列表可用於快取資料(例如,在Web伺服器上)。
7.散列表非常適合用於防止重複。(所以在python中,dict和set的key都是不可重複的。因為底層就是散列表)