數據結構和算法 - 線性表

數據結構和算法 - 線性表

線性表 是一種基礎的數據結構,顧名思義,線性表具有類似 一樣的性質。先給出一個定義:線性表是零個或者多個多個數據元素的 有限 序列

線性表的抽象數據類型

由線性表的定義,我們可以知道,對於一個線性表而言,長度是給定的,而且可以對線性表中的數據進行 插入和刪除 操作

線性表的抽象數據類型定義:

ADT 線性表
Data
Operation
	InitList(*L): 初始化線性表
	ListEmpty(L): 線性表是否爲空,空返回 true,否則返回 false
	ClearLits(*L): 清空線性表
	GetElem(L,i,*e): 將線性表的第 i 個元素返回給 e
	LocateElem(L,e): 在線性表中查找給定值 e 相等的元素,查找成功返回該元素在線性表的序號,否則返回 0 表示失敗
	ListInsert(*L,i,e): 在線性表第 i 個位置插入新元素 e
	ListDelete(*L,i,*e): 刪除線性表中的第 i 個元素,返回其值
	ListLenght(L): 返回線性表元素個數
end ADT

以上只是線性表的 基礎 操作,隨着不同線性表的具體實現可能有跟多更復雜的操作

線性表的存儲結構

線性表的存儲結構分爲基本的兩種:順序存儲鏈式存儲

順序存儲

鏈式存儲的基本定義爲:線性表的順序存儲結構是指,用一段 連續的 存儲單元存放線性表中 相同數據類型 數據元素

線性表的順序存儲結構中,首先要求存儲單元是連續的,其次是存放相同類型的數據。正是因爲存儲單元是連續的,所以,數組 這種線性表實現的基本數據結構就具有 隨機訪問 的特性,時間複雜度爲 O(1)

數組是如何實現隨機訪問的?

當我們在向系統申請一塊 數組 類型的結構空間的時候,系統會分配一塊連續的內存給我們,存儲器中每一塊內存單元都有自己的 地址編號,我們把第一塊地址稱爲 首地址,首地址的地址單元我們標記爲 base_address

由於數據類型是相同的,例如一個整型的線性表,每個元素所需要的 type_size 空間爲 4 個字節,我們就可以通過 尋址公式 直接訪問到 數組下標 爲 i 的數據存儲的地址:base_address + i * type_size

這樣就實現了根據數組下標隨機訪問的特性

同時,數組爲了維護空間的連續性,在 刪除和插入 操作的時候,就需要 額外的搬移數據 操作了。比如,在數據未滿的情況下,向第 i 個位置插入一個元素,此時需要從第 i 個元素開始,將其後面的元素一次往後搬移,直至騰出空間,將元素放入 i 的位置。這樣操作的時間複雜度爲 O(n)

刪除操作同理,在具體的實現過程中,我們可以不用每次刪除元素的時候都去挪動元素,而是將已刪除的數據標記爲 已刪除,等到數組滿了的時候再進行一次統一的刪除操作。這也是 JVM 垃圾回收中 標記清除算法 的思想

鏈式存儲

單鏈表的定義: 爲了表示元素與元素之間的邏輯關係,我們將順序表中每個數據元素拆分爲 數據域指針域。指針域中存儲下一個節點的地址。我們把由這兩部分組成的數據元素成爲 節點 ( Node )。由 n 個節點組成的順序表,每個節點中包含一個指針域,稱之爲 單鏈表

順序存儲的線性表 插入和刪除 操作十分低效,但是在鏈表中,插入和刪除就會變得十分高效(某些特定的插入和刪除,需要藉助 循環鏈表或雙向循環鏈表 實現)

單鏈表如何實現快速的插入和刪除操作?

我們知道,在單鏈表中,鏈表順序的維護是用指針,所以在插入和刪除的時候,我們只需要維護指針的指向而不需要去挪動元素就可以實現

刪除 node 節點後的節點:

$node->next = $node->next->next;

在 node 節點後插入新節點:

$newNode->next = $node->next;
$node->next = $newNode;

我們可以看到,在某些特定的插入和刪除操作下,單鏈表的時間複雜度爲 O(1),但是,如果要在鏈表中查詢某個節點,據需要遍歷整個鏈表,時間複雜度爲 O(n)

鏈表和數組的對比

鏈表適合插入和刪除操作,數組適合查找。在數組中,根據 下標隨機訪問 的時間複雜度爲 O(1)

時間複雜度 數組 鏈表
插入、刪除 O(n) O(1)
查找 O(1) O(n)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章