Android技能樹 — 數組,鏈表,散列表基礎小結

前言:

如今安卓面試,對於數據結構的問題也愈來愈多了,要求也愈來愈多,因此我對於數據結構只能慢慢補起來了。(灬ꈍ ꈍ灬)面試

Android技能書系列:算法

Android基礎知識數組

Android技能樹 — 動畫小結數據結構

Android技能樹 — View小結函數

Android技能樹 — Activity小結post

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

Android技能樹 — Android存儲路徑及IO操做小結3d

Android技能樹 — 多進程相關小結指針

Android技能樹 — Drawable小結cdn

數據結構基礎知識

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)

線性錶鏈式存儲(鏈表):

單鏈表:

不知道你們有沒有看過相似古墓麗影相似的探寶電影。

它們的步驟就是先知道到了一個地點,而後到了第一個目的地A,到了A以後根據線索才知道下一個目的地B在哪裏,而後再去B,而後這樣下去A-- B-- C --.....這樣,一直到最終的藏寶地方。沒錯,咱們的鏈表就是相似這種,好比咱們知道一共有四袋物品,可是你不能直接知道最後一個物品在哪裏,你只能從第一個開始,一個個找下去。

好比咱們第一個存在了01號抽屜,存儲內容爲A,同時告訴你們,下一個物品在05號抽屜,裏面內容爲B,同時再下一個在08號。

由上面的圖咱們能夠知道,結點由存放數據元素的數據域和存放後繼節點地址的指針域組成。

由上面咱們舉例的古墓麗影的劇情可知,咱們不能直接知道最後一個線索在哪裏,只能一個個從頭至尾查過去,因此鏈表的讀取會很慢;可是咱們若是想要插入和刪除就很方便。

好比咱們要插入一個新的結點:

好比咱們要刪除其中一個結點:

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

循環鏈表:

將單鏈表中終端結點的指針端改成指向頭結點,就使整個單鏈表造成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表,簡稱循環鏈表。

雙向鏈表:

雙向鏈表是在單鏈表的每一個結點中,再設置一個指向其前驅結點的指針域。

靜態鏈表:

靜態鏈表是爲了讓沒有指針的高級語言也可以用數組實現鏈表功能。

這個我就直接用網上的截圖來講明瞭:

靜態鏈表是用相似於數組方法實現的,是順序的存儲結構,在物理地址上是連續的,並且須要預先分配地址空間大小。因此靜態鏈表的初始長度通常是固定的。而後在這個裏面存的時候不只存儲數據域,同時存入了下一個數組index的位置。至關於咱們上面的指針域換成了數組的index值。

散列表(哈希表):

由上面咱們已經能夠知道數組和鏈表各自的優點和缺點了。

操做 數組 鏈表
讀取 擅長(能夠隨機/順序訪問) 不擅長(只能順序訪問)
插入/刪除 不擅長 擅長

有了上面的知識,咱們就能夠引入散列表了,咱們用具體的故事需求來引入散列表:

若是你有一天開了一家水果店,你會拿一個本子來記各類水果的價格,由於你們知道數組對於讀取來講很方便,因此咱們用一個數組來記錄各類水果的價格,而且是按照開頭字母來進行順序寫入的。

這時候,若是有人問Apple,你就查詢一下價格,可是若是水果不少,甚至不少都是A開頭的水果,好比有20個A開頭的水果,這時候你只能知道A開頭的水果是前面20個,可是具體是哪一個,你又要一個個的查過來,若是咱們立刻就知道Apple對應的數組index值就行了,這樣就立刻知道在數組的哪一個位置,而後立刻就能夠讀取出來了。

好比下圖:

這樣咱們就在index爲2的地方存儲了蘋果的價格,而後在index爲8的地方存儲了香蕉的價格,依次類推,全部水果都記錄進去,這樣顧客問你蘋果價格時候,你就立刻知道在index爲2的地方去讀取。

而把Apple變爲2是經過散列函數來實現的。

散列函數:

咱們要實現上面的需求,這個散列函數須要一些基本要求:

  1. 若是輸入的內容相同時,每次獲得的值都相同,好比你每次輸入都是Apple,好比每次獲得的結果都是2,不能一會兒2,一會兒5。

  2. 若是無論輸入什麼值獲得的結果都相同,那麼這個函數也沒用,你輸入Apple和輸入Banana獲得的值都相同,那麼沒有任何分辨做用。

  3. 散列函數須要返回有效的索引,好比上面咱們的數組的長度只有40,你輸入Pair時候輸出100,這樣是無效索引。

根據上面的狀況咱們知道了,咱們輸入不一樣的值的時候,經過散列函數換算後,最好的結果是每一個值都是不一樣,這樣的話他們的index 也不一樣。

可是若是咱們的數組只有長度爲6,可是咱們有7種水果,那麼必定會有二個水果獲得的index是相同的。這時候咱們稱這種狀況爲衝突

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

這樣,咱們在查詢其餘水果時候仍是很快,只是在查詢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。

參考:

《大話數據結構》

《算法圖解》

相關文章
相關標籤/搜索