如今安卓面試,對於數據結構的問題也愈來愈多了,要求也愈來愈多,因此我對於數據結構只能慢慢補起來了。(灬ꈍ ꈍ灬)面試
Android技能書系列:算法
Android基礎知識數組
Android技能樹 — Android存儲路徑及IO操做小結3d
數據結構基礎知識
算法基礎知識
本文主要講 數組,鏈表,散列表(哈希表)。
當咱們去看電影的時候,咱們知道電影院門口會有一個儲物櫃,
上面還會有連續的數字,一個抽屜連着一個抽屜。 而後你就會把你的東西放在相應號碼的小抽屜中,而後進去看電影了。
咱們在將數據存儲到內存時候,你請求計算機提供存儲空間,計算機會給你一個存儲地址,而後你把內容存進去。就相似上面的儲物櫃。
線性表:零個或多個數據元素的有限序列。
若是你有三袋東西,你一個抽屜只能存一袋東西,這時候你就可使用了連續三個櫃子。好比你使用了01,02,03號抽屜。
線性表的順序存儲結構:用一段地址連續的存儲單元依次存儲線性表的數據元素。
而後別人來使用了04號抽屜,這時候你朋友又給你一袋東西,說幫忙也去存一下,可是這時候由於04號抽屜已經被別人使用了,而大家又由於要求你們的東西都按照順序放在一塊兒,因此這時候大家只能從新找連續在一塊兒的抽屜,好比08,09,10,11。萬一12號被人使用了,而後大家又要再多存一袋物品呢??
這裏咱們看出數組的特色:
相似咱們在排隊買車票,忽然半路有我的插隊,大家全部人都須要日後退後了一位;最前面的人買好票走了一個,大家全部人均可以往前前進一位。
數組 | 時間複雜度 |
---|---|
讀取 | 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是經過散列函數來實現的。
咱們要實現上面的需求,這個散列函數須要一些基本要求:
若是輸入的內容相同時,每次獲得的值都相同,好比你每次輸入都是Apple,好比每次獲得的結果都是2,不能一會兒2,一會兒5。
若是無論輸入什麼值獲得的結果都相同,那麼這個函數也沒用,你輸入Apple和輸入Banana獲得的值都相同,那麼沒有任何分辨做用。
散列函數須要返回有效的索引,好比上面咱們的數組的長度只有40,你輸入Pair時候輸出100,這樣是無效索引。
根據上面的狀況咱們知道了,咱們輸入不一樣的值的時候,經過散列函數換算後,最好的結果是每一個值都是不一樣,這樣的話他們的index 也不一樣。
可是若是咱們的數組只有長度爲6,可是咱們有7種水果,那麼必定會有二個水果獲得的index是相同的。這時候咱們稱這種狀況爲衝突。
處理衝突的方式有不少,最簡單的辦法就是:若是二個鍵映射到了同一個位置,就在這個位置存儲一個鏈表。
這樣,咱們在查詢其餘水果時候仍是很快,只是在查詢index爲0的水果時候稍微慢一點,由於要在index爲0的鏈表中找到相應的水果。
散列表操做 | 平均狀況 | 最糟狀況 |
---|---|---|
查找 | O(1) | O(n) |
插入 | O(1) | O(n) |
刪除 | O(1) | O(n) |
咱們能夠看到:
因此針對最糟的狀況,咱們須要:
這樣咱們之後想要知道某個水果價格,只須要輸入水果名字,而後經過散列函數返回一個index值就能夠去數組中找相應的價格了。
哪裏錯誤請幫忙指正,thanks。
參考:
《大話數據結構》
《算法圖解》