1. 程式人生 > 程式設計 >資料結構與演演算法學習:陣列和連結串列

資料結構與演演算法學習:陣列和連結串列

陣列

陣列是一個線性表資料結構。它用一段連續的記憶體地址空間,來儲存一些相同型別的資料。 從上面的定義,我們不難看出幾個關鍵詞。

線性表:顧名思義,線性表就是資料排列成一條線的資料結構。每一個線性表只有前後兩個方向。佇列、連結串列、陣列、棧都是線性表結構。

連續的記憶體空間和相同型別的資料:正是因為有了這兩個限制,才使陣列有了,可以根據陣列的下標隨機訪問的特性。但是也因為連續的記憶體空間的限制,導致陣列在進行刪除和插入操作的時候,非常的低效。

如何進行陣列的隨機訪問?

首先我們定義一個陣列是,例如:int[] a=new int[10],當我們定義的時候,計算機會給陣列a[]分配一塊連續的記憶體地址。當我通過陣列下標去訪問時,計算機會通過定址公式a[i]_address = base_address + i * data_type_size

計算出資料在記憶體中的地址,進行訪問。其中,base_address是記憶體首地址,data_type_size表示陣列每個元素的大小。比如:啊a[]是int型別,所以data_type_size,就是4個位元組。

陣列的插入和刪除

為了保證陣列的連續記憶體空間性,在對中間的元素進行插入和刪除的時候,需要將陣列中其他元素移動位置。這樣的做法會浪費一些效能,與連結串列的插入和刪除相比,十分低效。

在一個有序的陣列中,往下標為3的位置插入元素,需要將下標4到末尾的資料整體向後挪移。如果在一個無序的陣列中,往下標為3的位置插入元素,最好的辦法是將下標為3的元素放到陣列的末尾,在將元素插入,減少元素挪到帶來的效能損耗。

和插入類似,刪除陣列中的一個元素,為了保證記憶體空間的連續性,需要進行資料的挪移。我們也可以記錄下每次刪除的元素,但不真正的去刪除挪移元素,到陣列已經滿了以後,在進行一次進行資料的刪除挪移。這樣可以大大減少元素搬移帶來的效能消耗。有點像垃圾回收機制。

陣列和容器

在JAVA中提供了陣列的型別的容器,比如:ArrayList。那麼陣列和ArrayList有哪些優缺點呢?

ArrayList最大的優點是將陣列的插入、刪除和查詢等一些操作進行了封裝,在使用的時候不需要考慮元素的搬移。另一個比較大的優點是,ArrayList支援動態擴容。由於在陣列在定義的時候,計算機需要根據陣列的大小分配一塊連續的記憶體空間,所以需要提前定義陣列的大小。ArrayList在底層進行了自動擴容實現,當元素大於ArrayList大小時,它會將空間擴大到1.5倍大小,然後將資料複製到新的陣列。

連結串列

連結串列是一種物理儲存單元上非連續、非順序的儲存結構,不需要一塊連續的記憶體空間,他通過連結串列的指標有次序的將零散的記憶體空間連線。最常見的連結串列結構有:單連結串列、雙向連結串列和迴圈連結串列。

單連結串列

單連結串列顧名思義,它的連結串列方向是單向的,對於連結串列的訪問只能從頭部開始,這也導致了連結串列的查詢效率低下。

上圖是一個單向連結串列,它有兩個結點比較特殊,一個是第一個結點,叫頭結點,用來記錄連結串列的基結點;另一個是最後一個結點,叫尾結點,它比較特殊,並不是指向下一個結點,而是指向一個NULL地址。

插入和刪除

從上面陣列我們知道,由於記憶體空間連續的限制,元素刪除和插入需要資料搬移。但是連結串列的記憶體空間不需要連續,所以它的插入和刪除操作十分的高效。

對於連結串列的查詢,我們只需要相鄰結點的指標變化。

上圖是一個單向連結串列的刪除過程,從圖上我們可以知道,將連結串列中的元素刪除,將這個元素刪除,然後,在將前結點的Next指標,指向它的後結點就OK了,十分的高效。插入操作也是類似,將前結點的Next指標指向,插入的元素,將插入的指標,指向它。

迴圈連結串列

迴圈連結串列是一種特殊的單連結串列。它與單連結串列的唯一區別是,尾結點的Next指標不是指向一個Null地址,而是,指向了連結串列的頭結點,形成了一個閉環,達到了迴圈的目的。其實確切的說這是一種單向迴圈連結串列。

雙向連結串列

與上面的單連結串列不同,雙向連結串列元素之間是雙向的,表現形式也比單連結串列複雜一點。它的每一個結點不止有後繼指標Next,還有一個前驅指標Pre。從這一點可知,一個雙向連結串列的結點比單向連結串列多一個前驅指標,這也意味著它的每一個結點需要更多的空間去儲存前驅指標。

插入和刪除操作與單向連結串列類似,只需要處理好前驅指標和後繼指標的指向。

單連結串列和雙向連結串列對比

我們知道,單向連結串列想要刪除一個元素,需要從頭部開始查詢直到找到該元素,然後進行刪除,指標從新指向,在這一點上雙向連結串列和單向連結串列是一致的。但是我們如果想要刪除一個元素的前驅結點,單向連結串列因為沒有前驅指標,需要再從頭遍歷,消耗時間,雙向連結串列就可以直接找到前驅結點,並刪除。

其實這是一種空間換時間的思路。使用了雙向連結串列佔用了多的空間,節約了時間。這種在我們記憶體足夠的情況下,優化演演算法的執行時間有很好的的幫助。

陣列與連結串列的對比

由於記憶體空間連續性的不同,連結串列適合插入、刪除操作,而陣列則更加適合根據下標的查詢。

最後,本文為學習記錄文,希望可以堅持下去。