數據結構與算法之線性表


前言

上一篇《數據結構和算法之時間復雜度和空間復雜度》中介紹了時間復雜度的概念和常見的時間復雜度,並分別舉例子進行了一一說明。這一篇主要介紹線性表。

線性表屬於數據結構中邏輯結構中的線性結構。回憶一下,數據結構分為物理結構和邏輯結構,邏輯結構分為線性結構、幾何結構、樹形結構和圖形結構四大結構。其中,線性表就屬於線性結構。剩余的三大邏輯結構今后會一一介紹。

線性表

基本概念

線性表(List):由零個或多個數據元素組成的有限序列。

注意:

1.線性表是一個序列。

2.0個元素構成的線性表是空表。

3.線性表中的第一個元素無前驅,最后一個元素無后繼,其他元素有且只有一個前驅和后繼。

4.線性表是有長度的,其長度就是元素個數,且線性表的元素個數是有限的,也就是說,線性表的長度是有限的。

如果用數學語言來進行定義,可如下:

若將線性表記為(a1,…,ai-1,ai,ai+1,…an),則表中ai-1領先於ai,ai領先於ai+1,稱ai-1是ai的直接前驅元素,ai+1是ai的直接后繼元素。

線性表基本操作

InitList(*L): 初始化操作,建立一個空的線性表L。

ListEmpty(L): 判斷線性表是否為空表,若線性表為空,返回true,否則返回false。

ClearList(*L): 將線性表清空。 GetElem(L,i,*e): 將線性表L中的第i個位置元素值返回給e。

LocateElem(L,e): 在線性表L中查找與給定值e相等的元素,如果查找成功,返回該元素在表中序號表示成功;否則,返回0表示失敗。

ListInsert(*L,i,e): 在線性表L中第i個位置插入新元素e。

ListDelete(*L,i,*e): 刪除線性表L中第i個位置元素,並用e返回其值。

ListLength(L): 返回線性表L的元素個數。

 

對於不同的應用,線性表的基本操作是不同的,上述操作是最基本的。

對於實際問題中涉及的關於線性表的更復雜操作,完全可以用這些基本操作的組合來實現。

兩種不同的線性表

我們知道,數據結構分為邏輯結構和物理結構,邏輯結構分為集合結構、線性結構、樹形結構和圖形結構四大類。物理結構分為順序存儲結構和鏈式存儲結構。我在之前寫的《數據結構和算法》中已經介紹過。

線性表是線性結構的一種,那么線性表當然也有物理結構,也就是說,線性表有兩種,分別是順序結構的線性表(叫做順序表)和鏈式結構的線性表(叫做鏈表)。

1.順序存儲結構的線性表

順序表是指順序存儲結構的線性表,指的是用一段地址連續的存儲單元依次存儲線性表的數據元素。

順序表表現在物理內存中,也就是物理上的存儲方式,事實上就是在內存中找個初始地址,然后通過占位的形式,把一定的內存空間給占了,然后把相同數據類型的數據元素依次放在這塊空地中。注意,這塊物理內存的地址空間是連續的。

舉 個例子,比如C語言中的基本變量的存儲就是連續的存儲在內存中的,比如聲明一個整數i,在64位系統中整數i在內存中占8字節,那么系統就會在內存中為這 個整型變量分配一個長度為8個字節的連續的地址空間,然后把這個i的二進制形式從高地址向低地址存儲,長度不足時候,最高位用0補齊。

順序表的結構體定義

#define MAXSIZE 20   // 順序表的最大存儲容量 typedef int ElemType; // 順序表存儲的數據類型  typedef struct { ElemType data[MAXSIZE]; // 用數組表示順序表 int length; // 線性表當前長度 } SqList;

 

通過上面用結構體定義順序表,我們可以看出順序表的封裝需要三個屬性:

1.存儲空間的起始位置。 數組data的存儲位置就是線性表存儲空間的存儲位置
2.線性表的最大存儲容量。數組長度MAXSIZE
3.線性表的當前長度。length
注意:數組的長度與線性表的當前長度是不一樣的。數組的長度是存放線性表的存儲空間的總長度,一般初始化后不變。而線性表的當前長度是線性表中元素的個數,是會改變的。

順序表查找元素操作

代碼實現:

順序表插入元素操作

思路如下:

 

1.如果插入位置不合理,拋出異常;

 

2.如果線性表長度大於等於數組長度,則拋出異常或動態增加數組容量;

 

3.從最后一個元素開始向前遍歷到第i個位置,分別將它們都向后移動一個位置;

 

4.將要插入元素填入位置i處;

5.線性表長+1。

代碼實現:

 

順序表刪除元素操作

 思路如下:

1.如果刪除元素的位置不合理,拋出異常。比如用戶刪除第0個位置的元素(線性表是從1開始的)、刪除元素的位置大於線性表的長度也要拋出異常。
2.刪除第i個位置的元素。
3.把第i個位置的元素后面的所有的元素的位置加一。
4.線性表長度減一。
代碼實現:

順序表優缺點

由以上代碼可以看出:

線性表的順序存儲結構,在存、讀取數據時,不管是在哪個位置,時間復雜度都是O(1)。而在插入或者刪除時,時間復雜度都是O(n)。
這也就是線性表的順序存儲結構比較適合存取數據,不適合經常插入和刪除數據的應用。
優點:
1.無需為了表示表中元素之間的邏輯關系而增加額外的存儲空間(相對於鏈式存儲而言)。
2.可以快速的存取表中任意位置的元素。
缺點:
1.插入和刪除操作需要移動大量的元素。
2.當線性表長度變化較大時,難以確定存儲空間的容量。
3.容易造成存儲空間的“碎片”(因為線性表的順序存儲結構申請的內存空間都以連續的,如果因為某些操作(比如刪除操作)導致某個部分出現了一小塊的不連續內存空間,因為這一小塊內存空間太小不能夠再次被利用/分配,那么就造成了內存浪費,也就是“碎片”)
PS:windows系統有磁盤碎片整理工具,而Linux系統沒有,因為Linux系統內核優化的很好,幾乎是沒有磁盤碎片的。
 

2.鏈式存儲結構的線性表

前面我們講的線性表的順序存儲結構,它最大的缺點就是插入和刪除時需要移動大量元素,這顯然就需要耗費時間。

那我們能不能針對這個缺陷或者說遺憾提出解決的方法呢?要解決這個問題,我們就得考慮一下導致這個問題的原因!

為什么當插入和刪除時,就要移動大量的元素?

原因就在於相鄰兩元素的存儲位置也具有鄰居關系,它們在內存中的位置是緊挨着的,中間沒有間隙,當然就無法快速插入和刪除。

線性表的鏈式存儲結構的特點是用一組任意的存儲單元存儲線性表的數據元素,這組存儲單元可以存在內存中未被占用的任意位置。

也就是說,鏈式存儲結構的線性表由一個(可以使零)或者多個結點(Node)組成。每個節點內部又分為數據域和指針域(鏈)。數據域存儲了數據元素的信息。指針域存儲了當前結點指向的直接后繼的指針地址。
因為每個結點只包含一個指針域,所以叫做單鏈表。顧名思義,當然還有雙鏈表。

單鏈表

鏈式存儲結構中,除了要存儲數據元素信息外,還要存儲它的后繼元素的存儲地址(指針)。

也就是說除了存儲其本身的信息外,還需存儲一個指示其直接后繼的存儲位置的信息。

我們把存儲數據元素信息的域稱為數據域,把存儲直接后繼位置的域稱為指針域。

 

指針域中存儲的信息稱為指針或鏈。

這兩部分信息組成數據元素稱為存儲映像,或稱為結點(Node)。

n個結點鏈接成一個鏈表,即為線性表(a1, a2, a3, …, an)的鏈式存儲結構。

 

因為此鏈表的每個結點中只包含一個指針域,所以叫做單鏈表。

 

對於線性表來說,總得有個頭有個尾,鏈表也不例外。我們把鏈表中的第一個結點的存儲位置叫做頭指針,最后一個結點指針為空(NULL)。

單鏈表是線性表中最具代表性的一種,下一篇文章中,本人將會拿出一章來介紹單鏈表,敬請期待!

圖片來源參考自:魚C工作室。感謝魚C工作室貢獻出了這么好的圖片。
如非特別說明,筆者所有文章都是原創文章。如果您喜歡這篇文章,轉載請注明出處。如果您對數據結構感興趣,請關注我,后續會更新大量精品文章供大家參考!

PS:本篇文章在簡書也有同步更新,大家也可以移步簡書關注本人,后續會更新更多精品文章!

簡書地址:http://www.jianshu.com/users/93131dfba96a/latest_articles


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM