閱讀本文大概需要 7 分鍾。
線性表是數據結構最最基本的一個概念,可是你真的了解線性表嗎?
線性表的存儲方式是什么?棧和隊列是線性表嗎?
如果能正確地回答這兩個問題,那么你就不用浪費時間看本文的內容了。否則,不管你覺得線性表是多么基礎的東西都還是花幾分鍾把本文看完吧。
本系列教程的接下來幾篇文章都是講數據結構的基礎,有人已經學過並掌握了,也有人沒學過或者早已忘記了。不管怎么樣,就當是復習一下吧。對於還沒有系統學過數據結構的同學,正好可以用碎片時間快速系統地學習一下。本系列教程關於數據結構的知識不會講每種數據結構的代碼實現,因為幾乎所有語言都內置實現好了。
基本概念
在本文開始之前,我們先了解一下線性表、順序表和鏈表這三個基本概念:
線性表(Linear List):線性表是最基礎、最簡單、最常用的一種基本數據結構。線性表存儲的每個數據稱為一個元素。一個線性表是 n 個具有相同特性的元素的有限序列。線性表有兩種存儲方式:順序存儲方式和鏈式存儲方式。
順序表(Sequence List):順序表就是線性表的順序存儲方式,以數組的形式保存。數組的順序存儲方式使得邏輯上相鄰的元素,其在物理存儲單元中也是相鄰的。並且數組是靜態分配的,即在使用數組之前需要分配固定大小的空間。可以通過索引直接得到數組中的元素,即獲取數組中元素的時間復雜度為 O(1)。
鏈表(Linked List):鏈表就是線性表的鏈式存儲方式。鏈表中的元素在內存中不是連續分配的,即邏輯上相鄰的兩個元素在物理存儲單元中不一定是相鄰的。而且它的分配是動態的,即創建鏈表時不需要事先指定大小。鏈表每個元素都過指針來指向下一個元素地址,將鏈表中的元素有序地串起來。
綜上,線性表有順序和鏈式兩種儲存方式,分別由順序表和鏈表這兩種數據結構實現。
順序表很簡單,它就是下標和元素一一對應的數組,沒什么好講的。下面重點講一下鏈表。
鏈表
鏈表(Linked List)是一種各元素按線性排列的數據結構。與數組不同的是,數組的線性順序由數組的下標決定,鏈表的順序是由各個元素的指針來決定的。鏈表主要分為三種:
-
單向鏈表(Singly Linked List)
-
雙向鏈表(Doubly Linked List)
-
循環鏈表(Circular Linked List)
三種鏈表的元素節點都包含數據和指針,它們的操作有插入、刪除和查找。當其中一個節點被刪除或在節點前插入一個新節點時,都需要改變相關節點的指針指向。
單向鏈表
單向鏈表又稱單鏈表,是鏈表中最簡單的一種。一個單向鏈表的元素節點被分成兩個部分。第一個部分存儲節點的信息,第二個部分存儲下一個節點的地址,最后一個節點則指向一個空值。一般第一個節點稱為鏈頭,最后一個節點稱為鏈尾。單向鏈表只可向一個方向遍歷。下面是單向鏈表的簡單示意圖:
一般查找一個節點的時候需要從第一個節點開始每次訪問下一個節點,一直訪問到需要的位置。但是也可以提前把一個節點的位置另外保存起來,然后直接訪問。
雙向鏈表
雙向鏈表又稱雙鏈表。相對於一個節點只有一處指針的單鏈表,雙鏈表中的一個節點有前、后兩個指針,通用分別用 Prev 和 Next 表示。Prev 指向前一個節點,稱為前驅,Next 指向后一個節點,稱為后繼。第一個節點的 Prev 指向空值或者空列表,最后一個節點的 Next 也指向空值或者空列表。下面是雙向鏈表的簡單示意圖:
查找一個結點可以從任何結點開始,每次可以訪問節點的前一個節點也可以訪問后一個節點。
循環鏈表
循環鏈表和單向鏈表、雙向鏈表基本一樣,區別是它的首節點和尾節點連接在了一起,即尾節點的 Next 指向的是首節點而不是空值,這種操作在單向和雙向鏈表中皆可實現。循環鏈表可以看着是無頭無尾單向或雙向鏈表。下面是循環鏈表的單向與雙向實現示意圖:
循環鏈表是閉環的,所以沒有鏈頭和鏈尾的說法。循環鏈表的設計對於算法插入操作更加容易,因為不需要考慮鏈頭和鏈尾的情況。
到這我們已經知道線性表存儲的存儲方式了。接下來我們再來看看堆棧和隊列這兩種數據結構。
棧和隊列
棧和隊列是我們常用的兩種數據結構,棧的后進先出和隊列的先進先出,我們早已耳熟能詳了。其實知道這點我覺得就夠了,只是你可能會好奇它們是不是屬於線性表呢?我先不直接回答這個問題,我們還是先看看棧和隊列是都是什么樣的結構和有什么操作吧。
棧
棧(Stack),又稱為堆棧,它實現的是一種后進先出(Last In First Out, LIFO)策略,被刪除的是最后插入的元素。棧的插入操作稱為壓入(Push),它的刪除操作稱為彈出(Pop)。棧有棧底(Bottom)和棧頂(Top),壓入和彈出都是在棧頂進行操作。下面是棧的簡單示意圖:
從圖中我們可以看到,棧中的元素是線性存儲的,元素的組成其實就是一個線性表,只是它被限制了只能在一端進行進出操作。棧既然是線性表,在具體應用中就可以用順序表或者鏈表來實現。
隊列
隊列(Queue)實現的是一種先進先出(First In First Out, FIFO)策略,被刪除的是在集合中最先插入的元素。隊列的插入操作稱為入隊(Enqueue),刪除操作稱為出隊(Dequeue)。隊列有隊頭(Head)和隊尾(Tail),當有一個新元素入隊時,它會被放在隊尾的位置。下面是隊列的簡單示意圖:
和棧一樣,隊列也是一種線性表,只是被限制在兩端進行進出操作,在具體應用中也可以用順序表或者鏈表來實現。
總結
線性表是一個大的概念。從存儲方式上來說它有順序存儲和鏈式存儲,分別對應順序表和鏈表兩種數據結構。棧和隊列都屬於線性表中的一種特例。
雖都是基礎知識,但看完本文,你有些許收獲嗎?反正我是挺有收獲的。
本文圖片來源:
http://t.cn/EZUNyFi
http://t.cn/EZUCgaV
本文參考:
算法導論,第三版,第 10 章