數組與鏈表——異同分析

       近期開始看《圖解算法》,雖然很早以前就接觸過算法這塊的,但還是不得不驚歎作者的功力,巧妙地藉助漫畫的形式和相關的例子把複雜的原理講得如此有趣和透徹。這裏將先跳過大O(時間複雜度、空間複雜度)相關部分的介紹,以及關於最佳、平均、最壞情況下複雜度的估計方法。


內存

首先是介紹內存的概念,一般來說,數據都是經硬盤加載到內存後纔可以進行後續處理(把硬盤空間大小當成內存空間大小的請面壁思過…)。那麼數據在內存中總得一個存儲方式吧,大致如下圖所示。可以將一個格子當做一個房間,一個房間裏可以進一個人(1byte),那麼總共有2^{64}個房間可以住。用稍微專業點話來講就是:對於64位計算機,理論上內存地址可用從0x00000000開始,一直到0xFFFFFFFF結束,而一個地址中有可以存儲一個1byte,總共有2^{64}個byte,約1.6\times 10^{7}Tb(夠大了吧,夠用了吧)。


那麼數據在內存中又是如何存放的呢?這裏不得不介紹兩個概念:順序存儲和隨機存儲,主要介紹將通過數組(代表順序存儲)和鏈表(代表隨機存儲)進行展開。

數組

數組的存儲方式爲順序存儲,簡單的來說,就是系統爲待存儲內容在內存中尋找一塊連續的空間(中間沒有跳躍),並將其存入。

例如,我們要將一串字符“abcdefg”存放到內存中,則存儲的方式可以如下所示。其中數據是連續存放,中間未出現過隔斷。那要訪問的話又是如何進行呢?如訪問數組的第四個元素,可以這麼寫a[3]。'a'的話則是a[0]。

**(可跳過這段)而爲什麼索引是從0而不是從1開始,這個就與計算地址偏移量的方式有關。用c語言的方式進行訪問:*(a+0)、*(a+3),其表示的是按照偏移量進行數據讀取的方法。

那麼事情來了,當我們要在'd'後後面加入兩個新字符"ee",這改怎麼辦呢?此時我們尷尬的發現,雖然僅僅是多了兩個字符,但原先8個byte的大小以及存放不下新的數據了!那麼我們只好釋放這塊內存,並在內存中重新尋找一塊新的連續空間進行存儲。換句話說,當我們和酒店說要預定一層所有的10個房間,但是這一層只要有一個房間在使用,我們就無法進行預定,只能換一層繼續尋找。而且當我們因其他因素需多預定2個房間時,因爲這個酒店一層最多隻有10個房間,不得不尋找新的合適的酒店。

刪除方法與插入的基本一致,這裏就不再描述。

數組的總結如下:可以進行隨機讀取,但在寫入時必須按順序存放

  數組
讀取 O(1)
插入 O(n)

鏈表

介紹完數據,那麼另一位大將也來了,就是鏈表,它是隨機存儲方式的代表,即存儲時可以對數據進行拆分,並在內存的任意位置進行存放。

接上文數據的事情,當我們需要存儲字符串“abcdefg”時,又該怎麼辦呢?圖中陰影部分表示空間被佔用,因此我們的數據可以跳躍着前進,在空餘的部分將數據存入。那麼將如何進行訪問呢?首先我們要找到起點‘a’的地址,並按照箭頭的指示方向依次進行前進。因爲隨機存儲的方式決定了數據的存放方式,即數據是被存放在內存中的不同位置,無法通過計算地址偏移量來獲取數據。

經過一番功夫,這個數據是存到內存裏了。和數組部分中介紹的那樣,當我們想在字符'd'後加兩個字符“ee”時,又該怎麼做呢?

此時,我們無需對上圖內容存儲的方式進行改變,只需在內存再找兩個未使用的地址,並改變字符'e'對下一節點的指向即可,如圖所示(請無視圖中歪歪扭扭的曲線…)。

刪除方法與插入的基本一致,這裏就不再描述了。

 鏈表的總結如下:可以進行隨機插入,但在讀取時必須順序查詢。

  鏈表
讀取 O(n)
插入 O(1)

總結時刻:

數組和鏈表都是數據存儲的一種手段,但兩者的存儲方式,致使各自在讀取、插入、刪除、查詢時的效率各有優劣,因而需要視具體情況選擇合適的存儲結構。

  數組 鏈表
讀取 O(1) O(n)
插入 O(n) O(1)
刪除 O(n) O(1)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章