1. 程式人生 > >資料結構--線性表和連結串列的基礎知識

資料結構--線性表和連結串列的基礎知識

近期準備重新學習一下常用資料結構和基本演算法,並計劃將這些內容的只是做一個整理和歸類,準備慢慢寫一個常用資料結構與基本演算法的系列博文,博文列表參見:常用資料結構與基本演算法博文系列,目前內容還比較少,後續慢慢補充。本文主要內容是介紹 資料結構--線性表和連結串列的基礎知識。

一 線性表概述

1.1 線性表概念

線性表,全名為線性儲存結構。使用線性表儲存資料的方式可以這樣理解,即“把所有資料用一根線兒串起來,再儲存到物理空間中”。

如上圖所示,這是一組具有“一對一”關係的資料,我們接下來採用線性表將其儲存到物理空間中。首先,用“一根線兒”把它們按照順序“串”起來,如圖下圖 所示:

1.2 線性表儲存結構

上圖中,左側是“串”起來的資料,右側是空閒的物理空間。把這“一串兒”資料放置到物理空間,我們可以選擇以下兩種方式,如下圖所示。

圖 3a) 是多數人想到的儲存方式,而圖 3b) 卻少有人想到。我們知道,資料儲存的成功與否,取決於是否能將資料完整地復原成它本來的樣子。如果把圖 3a) 和圖 3b) 線的一頭扯起,你會發現資料的位置依舊沒有發生改變(和圖 1 一樣)。因此可以認定,這兩種儲存方式都是正確的。上圖中我們可以看出,線性表儲存資料可細分為以下 2 種:

  1. 如圖 3a) 所示,將資料依次儲存在連續的整塊物理空間中,這種儲存結構稱為順序儲存結構(簡稱順序表);
  2. 如圖 3b) 所示,資料分散的儲存在物理空間中,通過一根線儲存著它們之間的邏輯關係,這種儲存結構稱為鏈式儲存結構(簡稱連結串列);

將具有“一對一”關係的資料“線性”地儲存到物理空間中,這種儲存結構就稱為線性儲存結構(簡稱線性表),線性表儲存結構可細分為順序儲存結構和鏈式儲存結構。使用線性表儲存的資料,如同向陣列中儲存資料那樣,要求資料型別必須一致,也就是說,線性表儲存的資料,要麼全不都是整形,要麼全部都是字串。一半是整形,另一半是字串的一組資料無法使用線性表儲存。(有一些弱型別程式語言(如python、MATLAB等)中的陣列或者列表中元素的資料型別可以不一致,這都是在我們今天講的基礎上進行擴充套件的)。

1.3 前驅和後繼

資料結構中,一組資料中的每個個體被稱為“資料元素”(簡稱“元素”)。例如,圖 1 顯示的這組資料,其中 1、2、3、4 和 5 都是這組資料中的一個元素。另外,對於具有“一對一”邏輯關係的資料,我們一直在用“某一元素的左側(前邊)或右側(後邊)”這樣不專業的詞,其實線性表中有更準確的術語:

  • 某一元素的左側相鄰元素稱為“直接前驅”,位於此元素左側的所有元素都統稱為“前驅元素”;
  • 某一元素的右側相鄰元素稱為“直接後繼”,位於此元素右側的所有元素都統稱為“後繼元素”;

以圖 1 資料中的元素 3 來說,它的直接前驅是 2 ,此元素的前驅元素有 2 個,分別是 1 和 2;同理,此元素的直接後繼是 4 ,後繼元素也有 2 個,分別是 4 和 5。如下圖4 所示: 

二 順序表

2.1 順序表的概念

 順序表,全名順序儲存結構,是線性表的一種。在前面的內容中我們提到,線性表用於儲存邏輯關係為“一對一”的資料,順序表自然也不例外。不僅如此,順序表對資料的物理儲存結構也有要求。順序表儲存資料時,會提前申請一整塊足夠大小的物理空間,然後將資料依次儲存起來,儲存時做到資料元素之間不留一絲縫隙。例如,使用順序表儲存集合 {1,2,3,4,5},資料最終的儲存狀態如下圖所示:


由此我們可以得出,將“具有 '一對一' 邏輯關係的資料按照次序連續儲存到一整塊物理空間上”的儲存結構就是順序儲存結構。通過觀察上圖中資料的儲存狀態,我們可以發現,順序表儲存資料同陣列非常接近。其實,順序表儲存資料使用的就是陣列。

 2.2 順序表的基本操作

 順序表的基本操作包括順序表的初始化、插入元素、刪除元素、更改元素、查詢元素等。這些基本操作的很簡單,就不一一解釋了,具體程式碼後續加上。

三 連結串列

3.1 連結串列的概念

連結串列,別名鏈式儲存結構或單鏈表,用於儲存邏輯關係為 "一對一" 的資料。與順序表不同,連結串列不限制資料的物理儲存狀態,換句話說,使用連結串列儲存的資料元素,其物理儲存位置是隨機的。例如,使用連結串列儲存 {1,2,3},資料的物理儲存狀態如下圖所示:

我們看到,上圖根本無法體現出各資料之間的邏輯關係。對此,連結串列的解決方案是,每個資料元素在儲存時都配備一個指標,用於指向自己的直接後繼元素。如下圖所示,資料元素隨機儲存,並通過指標表示資料之間邏輯關係的儲存結構就是鏈式儲存結構。

3.2 連結串列的節點

從上圖可以看到,連結串列中每個資料的儲存都由以下兩部分組成,即連結串列中儲存各資料元素的結構如下圖所示:

  1. 資料元素本身,其所在的區域稱為資料域;
  2. 指向直接後繼元素的指標,所在的區域稱為指標域;

上圖所示的結構在連結串列中稱為節點。也就是說,連結串列實際儲存的是一個一個的節點,真正的資料元素包含在這些節點中,如下圖所示:

 

3.3 連結串列的組成要素

其實,上圖所示的連結串列結構並不完整。一個完整的連結串列需要由以下幾部分構成:
  1. 頭指標:一個普通的指標,它的特點是永遠指向連結串列第一個節點的位置。很明顯,頭指標用於指明連結串列的位置,便於後期找到連結串列並使用表中的資料;
  2. 節點:連結串列中的節點又細分為頭節點、首元節點和其他節點:
    • 頭節點:其實就是一個不存任何資料的空節點,通常作為連結串列的第一個節點。對於連結串列來說,頭節點不是必須的,它的作用只是為了方便解決某些實際問題;
    • 首元節點:由於頭節點(也就是空節點)的緣故,連結串列中稱第一個存有資料的節點為首元節點。首元節點只是對連結串列中第一個存有資料節點的一個稱謂,沒有實際意義;
    • 其他節點:連結串列中其他的節點;
因此,一個儲存 {1,2,3} 的完整連結串列結構如下圖 所示:

注意:連結串列中有頭節點時,頭指標指向頭節點;反之,若連結串列中沒有頭節點,則頭指標指向首元節點。

3.4 連結串列的分類

連結串列根據其前驅、後繼的索引特徵可以分為單向連結串列、雙向連結串列和迴圈連結串列三類。 單向連結串列:表中各節點中都只包含一個指標(遊標),且都統一指向直接後繼節點,通常稱這類連結串列為單向連結串列(或單鏈表)。單鏈表的資料塊和連結串列形式就是我們前面介紹的。 雙向連結串列:表中各各節點之間的邏輯關係是雙向的,節點中都包含兩個指標,一個指向直接後繼節點、一個指向直接前驅節點,通常稱這類連結串列為雙向連結串列(或雙鏈表)。雙向連結串列中各節點包含以下 3 部分資訊(如下圖所示):
  1. 指標域:用於指向當前節點的直接前驅節點;
  2. 資料域:用於儲存資料元素。
  3. 指標域:用於指向當前節點的直接後繼節點;

迴圈連結串列:表中頭尾節點相連,構成一個環。需要注意的是,雖然迴圈連結串列成環狀,但本質上還是連結串列,因此在迴圈連結串列中,依然能夠找到頭指標和首元節點等。迴圈連結串列和普通連結串列相比,唯一的不同就是迴圈連結串列首尾相連,其他都完全一樣。迴圈連結串列又可以分為單向迴圈連結串列和雙向迴圈連結串列。

3.5 連結串列的基本操作

連結串列的基本操作與順序表一樣,包括連結串列的初始化、插入元素、刪除元素、更改元素、查詢元素等。這些基本操作的含義很簡單,就不一一解釋了,具體程式碼後續加上。

3.5.1 單鏈表的基本操作

插入元素操作:向單鏈表中增添元素,根據新增位置不同,可分為以下 3 種情況:

  • 插入到連結串列的頭部(頭節點之後),作為首元節點;
  • 插入到連結串列中間的某個位置;
  • 插入到連結串列的最末端,作為連結串列中最後一個數據元素;

雖然新元素的插入位置不固定,但是連結串列插入元素的思想是固定的,只需做以下兩步操作,即可將新元素插入到指定的位置:

  1. 將新結點的 next 指標指向插入位置後的結點;
  2. 將插入位置前結點的 next 指標指向插入結點;

例如,我們在連結串列 {1,2,3,4} 的基礎上分別實現在頭部、中間部位、尾部插入新元素 5,其實現過程如下圖所示:

 

刪除元素操作:從連結串列中刪除指定資料元素時,實則就是將存有該資料元素的節點從連結串列中摘除,但作為一名合格的程式設計師,要對儲存空間負責,對不再利用的儲存空間要及時釋放。因此,從連結串列中刪除資料元素需要進行以下 2 步操作:

  1. 將結點從連結串列中摘下來;
  2. 手動釋放掉結點,回收被結點佔用的儲存空間;

例如,從存有 {1,2,3,4} 的連結串列中刪除元素 3,則此程式碼的執行效果如下圖所示:

3.5.2 雙鏈表的基本操作

插入元素操作:雙向連結串列的插入元素的操作與單向連結串列基本一樣,也是分為三種情況,只是在插入元素時需要考慮前驅和後繼兩個指標的變化。

 

  • 新增至表頭:

  • 新增到中間位置:

  • 新增到末尾:

刪除元素操作:雙鏈表刪除結點時,也是跟單向連結串列一樣,只需遍歷連結串列找到要刪除的結點,然後將該節點從表中摘除即可。

&n