Android技能樹 — 陣列,連結串列,雜湊表基礎小結

青蛙要fly發表於2018-04-23

前言:

現在安卓面試,對於資料結構的問題也越來越多了,要求也越來越多,所以我對於資料結構只能慢慢補起來了。(灬ꈍ ꈍ灬)

Android技能書系列:

Android基礎知識

Android技能樹 — 動畫小結

Android技能樹 — View小結

Android技能樹 — Activity小結

Android技能樹 — View事件體系小結

Android技能樹 — Android儲存路徑及IO操作小結

Android技能樹 — 多程式相關小結

Android技能樹 — Drawable小結

資料結構基礎知識

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

Android技能樹 — 樹基礎知識小結(一)

演算法基礎知識

Android技能樹 — 排序演算法基礎小結

本文主要講 陣列,連結串列,雜湊表(雜湊表)。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

當我們去看電影的時候,我們知道電影院門口會有一個儲物櫃,

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

上面還會有連續的數字,一個抽屜連著一個抽屜。 然後你就會把你的東西放在相應號碼的小抽屜中,然後進去看電影了。

我們在將資料儲存到記憶體時候,你請求計算機提供儲存空間,計算機會給你一個儲存地址,然後你把內容存進去。就類似上面的儲物櫃。


線性表

線性表:零個或多個資料元素的有限序列。

線性表順序儲存(陣列):

如果你有三袋東西,你一個抽屜只能存一袋東西,這時候你就可以使用了連續三個櫃子。比如你使用了01,02,03號抽屜。

線性表的順序儲存結構:用一段地址連續的儲存單元依次儲存線性表的資料元素。

然後別人來使用了04號抽屜,這時候你朋友又給你一袋東西,說幫忙也去存一下,但是這時候因為04號抽屜已經被別人使用了,而你們又因為要求大家的東西都按照順序放在一起,所以這時候你們只能重新找連續在一起的抽屜,比如08,09,10,11。萬一12號被人使用了,然後你們又要再多存一袋物品呢??

這裡我們看出陣列的特點:

  1. 如果我們有四袋物品,我們已經知道了第一袋物品在N號碼的抽屜,那麼其他三個肯定在N+1,N+2,N+3號,所以在查詢的時候十分方便,因為我們只需要知道一個的位置,其他的位置都知道了。(所以查詢起來很方便,因為所有的位置都知道具體在哪個)
  2. 如果我們把A放在01,B放在02,C放在03,這時候我們說在A和B之間插入一個D,這時候我們需要把B和C都往後移動。同理刪除一個也是一樣,比如我們刪除了A,B和C都要往前移動。(所以插入刪除比較麻煩,需要移動所有後面位置的資料)
  3. 如果你突然多了一個需要儲存的物品,而且已經不夠放了,那麼需要全部重新移動到新的連續的儲存地方。

類似我們在排隊買車票,突然半路有個人插隊,你們所有人都需要往後退後了一位;最前面的人買好票走了一個,你們所有人都可以往前前進一位。

陣列 時間複雜度
讀取 O(1)
插入/刪除 O(n)

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

線性錶鏈式儲存(連結串列):

單連結串列:

不知道大家有沒有看過類似古墓麗影類似的探寶電影。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

它們的步驟就是先知道到了一個地點,然後到了第一個目的地A,到了A之後根據線索才知道下一個目的地B在哪裡,然後再去B,然後這樣下去A-- B-- C --.....這樣,一直到最終的藏寶地方。沒錯,我們的連結串列就是類似這種,比如我們知道一共有四袋物品,但是你不能直接知道最後一個物品在哪裡,你只能從第一個開始,一個個找下去。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

比如我們第一個存在了01號抽屜,儲存內容為A,同時告訴大家,下一個物品在05號抽屜,裡面內容為B,同時再下一個在08號。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

由上面的圖我們可以知道,結點由存放資料元素的資料域和存放後繼節點地址的指標域組成。

由上面我們舉例的古墓麗影的劇情可知,我們不能直接知道最後一個線索在哪裡,只能一個個從頭到尾查過去,所以連結串列的讀取會很慢;但是我們如果想要插入和刪除就很方便。

比如我們要插入一個新的結點:

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

比如我們要刪除其中一個結點:

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

連結串列 時間複雜度
讀取 O(n)
插入/刪除 O(1)

迴圈連結串列:

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

將單連結串列中終端結點的指標端改為指向頭結點,就使整個單連結串列形成一個環,這種頭尾相接的單連結串列稱為單迴圈連結串列,簡稱迴圈連結串列。

雙向連結串列:

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

雙向連結串列是在單連結串列的每個結點中,再設定一個指向其前驅結點的指標域。

靜態連結串列:

靜態連結串列是為了讓沒有指標的高階語言也能夠用陣列實現連結串列功能。

這個我就直接用網上的截圖來說明了:

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

靜態連結串列是用類似於陣列方法實現的,是順序的儲存結構,在實體地址上是連續的,而且需要預先分配地址空間大小。所以靜態連結串列的初始長度一般是固定的。然後在這個裡面存的時候不僅儲存資料域,同時存入了下一個陣列index的位置。相當於我們上面的指標域換成了陣列的index值。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

雜湊表(雜湊表):

由上面我們已經可以知道陣列和連結串列各自的優勢和缺點了。

操作 陣列 連結串列
讀取 擅長(可以隨機/順序訪問) 不擅長(只能順序訪問)
插入/刪除 不擅長 擅長

有了上面的知識,我們就可以引入雜湊表了,我們用具體的故事需求來引入雜湊表:

如果你有一天開了一家水果店,你會拿一個本子來記各種水果的價格,因為大家知道陣列對於讀取來說很方便,所以我們用一個陣列來記錄各種水果的價格,並且是按照開頭字母來進行順序寫入的。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

這時候,如果有人問Apple,你就查詢一下價格,但是如果水果很多,甚至很多都是A開頭的水果,比如有20個A開頭的水果,這時候你只能知道A開頭的水果是前面20個,但是具體是哪個,你又要一個個的查過來,如果我們馬上就知道Apple對應的陣列index值就好了,這樣就馬上知道在陣列的哪個位置,然後馬上就可以讀取出來了。

比如下圖:

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

這樣我們就在index為2的地方儲存了蘋果的價格,然後在index為8的地方儲存了香蕉的價格,依次類推,所有水果都記錄進去,這樣顧客問你蘋果價格時候,你就馬上知道在index為2的地方去讀取。

而把Apple變為2是通過雜湊函式來實現的。

雜湊函式:

我們要實現上面的需求,這個雜湊函式需要一些基本要求:

  1. 如果輸入的內容相同時,每次得到的值都相同,比如你每次輸入都是Apple,比如每次得到的結果都是2,不能一下子2,一下子5。

  2. 如果不管輸入什麼值得到的結果都相同,那麼這個函式也沒用,你輸入Apple和輸入Banana得到的值都相同,那麼沒有任何分辨作用。

  3. 雜湊函式需要返回有效的索引,比如上面我們的陣列的長度只有40,你輸入Pair時候輸出100,這樣是無效索引。

根據上面的情況我們知道了,我們輸入不同的值的時候,通過雜湊函式換算後,最好的結果是每個值都是不同,這樣的話他們的index 也不同。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

但是如果我們的陣列只有長度為6,但是我們有7種水果,那麼一定會有二個水果得到的index是相同的。這時候我們稱這種情況為衝突

處理衝突的方式有很多,最簡單的辦法就是:如果二個鍵對映到了同一個位置,就在這個位置儲存一個連結串列。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

這樣,我們在查詢其他水果時候還是很快,只是在查詢index為0的水果時候稍微慢一點,因為要在index為0的連結串列中找到相應的水果。

雜湊表操作 平均情況 最糟情況
查詢 O(1) O(n)
插入 O(1) O(n)
刪除 O(1) O(n)

我們可以看到:

  1. 雜湊表的查詢(獲取給定索引處的值)速度與陣列一樣快,而插入和刪除速度與連結串列一樣快。因此它具備了二者的有點
  2. 但是最糟情況下,雜湊表的各種操作速度都很慢(比如都集中在index為0的連結串列下面,則查詢就跟連結串列查詢一樣了。)

所以針對最糟的情況,我們需要:

  1. 較低的填裝因子: 雜湊表使用陣列來儲存資料,因此需要計算陣列中被佔用的位置數。 (比如,陣列的長度為10,我們填入的數佔用了其中三個,則填裝因子為0.3;如果填入的數正好把長度佔滿,則填裝因子為1;如果填入了20個,則填裝因子為2。) 當填裝因子太大了,說明陣列長度不夠了,我們就要再雜湊表中新增位置了。稱為調整長度。(一旦填裝因子大於0.7就調整雜湊表的長度,為此你首先建立一個更長的新陣列,通常將陣列增長一倍)
  2. 良好的雜湊函式: 良好的雜湊好書讓陣列中的值呈均勻分佈,糟糕的雜湊函式讓值扎堆,導致大量的衝突。

這樣我們以後想要知道某個水果價格,只需要輸入水果名字,然後通過雜湊函式返回一個index值就可以去陣列中找相應的價格了。

結語:

哪裡錯誤請幫忙指正,thanks。

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

參考:

《大話資料結構》

《演算法圖解》

相關文章