1. 程式人生 > >線性資料結構——線性表

線性資料結構——線性表

資料結構

資料結構可以用一個二元組來定義,DataStructure = (D,S)。其中D是資料元素的有限集,SD上關係的集合。這個定義是數學上一種描述,這裡的關係代表著資料元素之間的邏輯關係,即邏輯結構,通常指集合、線性表、樹、圖四種結構。另一方面,某種資料結構關係在計算機中的具體排列情況又稱為物理結構,分為順序儲存結構鏈式儲存結構。任何邏輯結構都只能由這兩種物理結構來實現,選擇不同的物理結構得到的效能和優點也各不相同。

線性表

直觀的講線性表就是以一一對應關係排列下去的有限序列。除首位外,每個元素存在唯一的前驅,除末位外,每個元素存在唯一的後繼。下面從增刪查改這四個主要操作入手,分別講解順序儲存和鏈式儲存下的線性表。

一、順序儲存線性表

高階語言中的陣列符合在記憶體中連續的特點,通常以其作為實現順序儲存的載體。陣列的特點是可以在O(1)時間內讀寫任意一個位置的資料,而插入和刪除操作則需要移動大量的元素,導致時間複雜度增加為O(n)。

在長度為n的順序表的第i個位置前插入一個元素,我們要做的是將第i個以及之後的所有元素整體向後移動一位,可以想象老式手機滑動露出鍵盤的過程,如圖所示。

                                                            

下面來做一點數學,計算插入操作所需的平均時間。在第i個位置插入元素的概率為p_{i},這之後要移動的元素為n-i+1個,那麼插入所需時間的期望值為

                                                                    E_{insert}=\sum _{i=1}^{n+1}p_{i}(n-i+1)

假設元素插入到每個位置的概率相等,那麼p_{i}=\frac{1}{n+1}(多1是因為可以插入到最後一個沒有元素的位置),代入後計算得到E_{insert}=\frac{n}{2},故插入操作得時間複雜度為O(n)。

刪除長度為n的順序表的第i個位置的元素,操作與插入類似,只不過刪除是將第i個之後(不包括第i個)的所有元素都向前移動一位,從而覆蓋第i個元素實現刪除的效果。

同樣,我們計算一下所需時間的期望值,刪除的是第i個元素的概率為p_{i},要移動的元素個數為n-i,期望值為

                                                                     E_{delete}=\sum _{i=1}^{n}p_{i}(n-i)

仍假設刪除各個位置的元素的概率相等,p_{i}=\frac{1}{n},代入得到E_{delete}=\frac{n-1}{2},故刪除操作的時間複雜度為O(n)。

前面已闡述過順序儲存的特點,即可以實現隨機訪問,故讀取任意位置的元素的時間複雜度為O(1)。具體而言,在陣列中我們可以通過下標來直接獲取某個位置的元素。

同理,寫入也可以通過隨機訪問實現,其時間複雜度為O(1)。

二、鏈式儲存線性表

鏈式儲存主要是通過一種包含資料域指標域的資料結構作為結點連結而成。每個結點除了儲存有這個結點表示的資料,還另外存有一個指標用於指向下一個結點(這裡僅討論單向連結串列)。特別地,我們把第一個結點叫做頭結點,並將一個指向它的指標儲存下來,作為對連結串列進行各種操作的線索,稱為頭指標。鏈式儲存的特點是它在記憶體空間中是非連續的,要訪問任意一個結點只能從頭開始,循著指標一路走到這個結點的位置,因此讀取和寫入的時間複雜度均為O(n)。

在長度為n的連結串列的第i位之前插入一個元素,記第i個結點為this,並構造一個新的結點,稱它為new,將要插入的資料賦值到new的資料域。假設我們已經找到this的前一個結點pre(而不需要知道this),那麼插入操作可以由以下步驟實現:

(1)將new的指標指向pre當前指向的結點(即this)

(2)修改pre的指標,令其指向new

具體的程式碼實現可以在網上任意找到參照,這裡就不再贅述,僅關注其中運用的思想和分析演算法效能。顯然,上面的步驟總數是固定不變的,與連結串列長度無關,故在已知前一個結點的情況下,插入操作的時間複雜度為O(1)。

然而一般來講,我們是無法直接獲取到插入位置的前一個結點的,這需要從頭結點開始,不斷訪問下一個結點,直到第i-1個結點,該過程經歷了i-1步操作。假設插入在每個位置的概率是相等的,即 p_{i}=\frac{1}{n+1} ,由此可知尋找前驅結點的複雜度期望值為

                                                                       E=\sum _{i=1}^{n+1}p_{i}(i-1)=\frac{n}{2}

即尋找前驅結點時間複雜度為O(n),那麼插入過程的總的時間複雜度為O(n)+O(1)=O(n)。

刪除長度為n的連結串列中的第i位,我們同樣記第i個結點為this,其前驅為pre,在已找到pre的情況下,刪除操作如下:

(1)將pre的指標指向this的下一位

(2)刪除this結點

上述過程的時間複雜度為O(1),而尋找前驅的時間複雜度為O(n),故刪除操作的時間複雜度總共為O(n)。

分析讀取連結串列的第i位的複雜度,我們可以借用上面的尋找第i位前驅結點的結論。首先找到第i個結點的前驅用時為O(n),然後僅需一步操作即可找到第i個結點,故其總時間複雜度仍為O(n)。

修改第i位的資料與讀取類似,都只需找到該結點即可,時間複雜度同樣為O(n)。