資料結構 鍊錶 鄰接表

2021-09-11 17:25:09 字數 2969 閱讀 2379

在講解這個內容之前,先來回憶陣列能夠完成哪些操作:

(1)任意訪問乙個元素,時間複雜度為o(1

)o(1)

o(1)

;(2)刪除乙個元素,要移動此後的所有元素,時間複雜度為o(n

)o(n)

o(n)

;(3)在任意位置之後增添乙個元素,要移動此後的所有元素,時間複雜度為o(n

)o(n)

o(n)

;但如果我們的問題並不需要訪問任意元素,只需要進行刪除和新增,或者一些簡單的求前驅後繼的問題,用陣列顯然是不划算的。當有m個操作時,時間複雜度為o(n

m)

o(nm)

o(nm)。

您需要寫一種資料結構,維護乙個序列,初始序列中只有乙個編號為1

11的數。要求提供四種操作:

(1)格式:111x

xxy

yy,表示在編號為x

xx的數後新增乙個編號為y

yy的數;

(2)格式:222x

xx,表示刪除編號為x

xx的數;

(3)格式:333x

xx,表示輸出編號為x

xx的數的前乙個數的編號;

(4)格式:444x

xx,表示輸出編號為x

xx的數的後乙個數的編號。

陣列的特點是任意訪問,但無法任意增添。所以我們需要一種資料結構來滿足任意增添的要求。

陣列在空間上是連續的,所以加入或刪除需要移動大量元素,如果我們儲存序列的記憶體空間是離散的,那麼久可以避免這樣乙個問題。這道題中,我們不以乙個單獨陣列的形式來儲存資料,而是用一種比較抽象的邏輯結構來維護。

對於這樣乙個線性表,我們可以在每個位置建立乙個指向前乙個元素和指向後乙個元素的指標,這樣我們將key-value的關係轉換了,轉化成了乙個從頭到尾乙個接乙個串聯起來的結構。

本來陣列的一塊連續的記憶體中間是沒有空隙的,所以無法在中間直接插入元素,但是使用這種鍊錶的方式可以方便的在兩個前後的元素之間插入,只需改變前後元素指向的位置即可。

在這裡,我們用pre[i]表示i的前乙個數字,nx[i]表示i的後乙個數字,來模擬指標的效果。

當我們需要在i和nx[i]之間插入乙個元素x時,只需要執行:

pre[x]

=i; nx[x]

=nx[i]

; pre[nx[i]

]=x;

nx[i]

=x;

改變四組邏輯關係,斷開i與nx[i]之前的關係,連線i與x,x與nx[i]。要注意操作順序。

當我們刪除i時,只需要執行:

nx[pre[i]

]=nx[i]

;pre[nx[i]

]=pre[i]

;

將i與pre[i]、nx[i]之間的連線斷開,連線pre[i]與nx[i]。求前驅和後繼就更為簡單,直接輸出pre[i]或nx[i]即可。

來看一道鍊錶的典型例題:

luogu 1160 佇列安排

這道題要順次安排所有人的位置,放在某個人之前或之後,然後刪除若干個人,最後輸出整個結果,需要支援任意位置的加入,因此用鍊錶實現。

另乙個問題是如何來實現鍊錶的順次遍歷,其實我們只需要記錄第乙個首位元素,並從首尾元素每次訪問它的下乙個元素,就實現了鍊錶的遍歷。所以這題其實可以作為鍊錶的乙個模板題來做…

**中,刪除用了乙個更簡單的方法,直接標記刪除的數vis為1,則遍歷時若數的vis值為1直接跳過即可。

#include

using

namespace std;

int n,m,x,y,cur,pre[

100010

],nx[

100010

],vis[

100010];

int main ()}

else

}scanf

("%d"

,&m)

;for

(int i=

1;i<=m;i++

)for

(int i=cur;i;i=nx[i])}

return0;

}

鄰接表其實是由多個表頭和每個表頭對應的鍊錶組成的,某個表頭對應的鍊錶可能為空。

鄰接表的主要用途有儲存樹狀或圖狀結構,或建立hash表。這裡主要講講前者。

例如我們需要建立一張圖,以點的編號作為每乙個表頭的索引,每個點的出邊為鍊錶元素,那麼我們用hd[i]表示i的表頭,ver[i]和edge[i]分別描述i邊的指向和長度,那麼建邊函式add_edge如下:

int ver[

2*maxn]

,nx[

2*maxn]

,edge[

2*maxn]

,hd[maxn]

,tot;

void add_ (

int u,

int v,

int w)

for

(int i=hd[r]

;i;i=nx[i]

)

即可,此時就可以遍歷每一條以r為出發點,ver[i]為到達點,長度為edge[i]的邊了。

這是用陣列模擬鍊錶的形式來實現鄰接表,我們還可以使用vector來簡化過程。考慮簡單的情況,邊不帶有權值,那麼我們以v[i]表示以i為出發點的所有邊的集合,那麼如果從i到j連一條有向邊,即是:

v[i]

.push_back

(j);

如果我們需要訪問以i為出發點的所有邊,只需要遍歷v[i]即可。

鍊錶和陣列是兩種類似而又有區別的線性表結構,陣列能夠快速訪問,但不能快速增刪;鍊錶能夠快速增刪,卻不能快速根據key訪問value,所以就會出現這麼一種將鍊錶與陣列互補的資料結構——塊狀鍊錶,能夠使得各種操作的複雜度都為o(n

)o(\sqrt n)

o(n​)。

資料結構 鄰接多重表

上一節總結了有向圖的另外一種鏈式儲存結構 十字鍊錶,該節繼續總結無向圖的另一種鏈式儲存結構。鄰接表雖然已經能夠很好地表示無向圖了,但是無向圖訪問或者刪除一條邊 vi,vj 時需要同時訪問兩個鍊錶i和j並分別找到對應的邊結點,這給針對圖的邊的操作 標記或刪除 帶來不便利。鄰接多重表因此而演變過來的。鄰...

資料結構 鍊錶 雙向鍊錶

注意typedef的定義結構,以及dinklist的資料型別 typedef struct dnode dnode,dinklist 注意插入第乙個結點時,prior指標的空指向問題 if l next null 若l後繼結點為空 則省略該步驟 l next prior p 基本 頭插法建立雙向鍊錶...

資料結構鍊錶 靜態鍊錶

1 在這裡我們首先要複習一下鍊錶c語言的定義 這看起來很簡單,但實際上至關重要!data域 存放結點值的資料域。next域 存放結點的直接後繼的位址 位置 的指標域 鏈域 也就是說next域儲存的是乙個位址,這個位址是下一節點的位址。注意 鍊錶通過每個結點的鏈域將線性表的n個結點按其邏輯順序鏈結在一...