C语言数据结构、十字链表的分析及实现
目錄
前言
一、什么是十字鏈表
二、認識十字鏈表
1.十字鏈表的組成
2.頂點和弧的連接
三、代碼邏輯實現
1.出度
?2.入度
總結
前言
? 無論是什么程序都要和數據打交道,一個好的程序員會選擇更優的數據結構來更好的解決問題,因此數據結構的重要性不言而喻。數據結構的學習本質上是讓我們能見到很多前輩在解決一些要求時間和空間的難點問題上設計出的一系列解決方法,我們可以在今后借鑒這些方法,也可以根據這些方法在遇到具體的新問題時提出自己的解決方法。(所以各種定義等字眼就不用過度深究啦,每個人的表達方式不一樣而已),在此以下的所有代碼都是僅供參考,并不是唯一的答案,只要邏輯上能行的通,寫出來的代碼能達到相同的結果,并且在復雜度上差不多,就行了。
一、什么是十字鏈表
在上篇文章的最后,我們分析了鄰接表的優劣,鄰接表本身并沒有什么大的缺陷,如果說有缺點,那么是對于有向圖而言對同時表示一個頂點的出度和入度麻煩,因為需要有鄰接表和逆鄰接表同時表示,而且這種應用場景是存在的。十字鏈表就是為了使這個問題得到解決而出現的。
所以十字鏈表就是一種將鄰接表和逆鄰接表結合在一起的一種圖的存儲結構,它針對的就是有向圖中出度和入度一起使用的情況,并且大大節省了內存。
二、認識十字鏈表
1.十字鏈表的組成
十字鏈表是由數組+鏈表的形式構成的,數組用來記錄頂點的信息,鏈表用來記錄弧(邊)的信息。
在有向圖中,頂點集的存儲結構如下圖所示
其中data是數據域,用于記錄頂點的信息,而firstIn和firstOut是兩個指針,指向的是以該節點為弧頭(In)或弧尾(Out)的第一節點。
?對于如此一個有向圖而言,弧頭和弧尾的概念是人為設置的,在這里我們假定一個頂點的入度指向弧頭,出度的方向為弧尾
?它的頂點數組應該這么表示,為了方便講解,這里我們假定輸入的值就是對應的下標,實際上應該通過用戶輸入的值在頂點數組中找到對應的下標
代碼實現
//頂點集 typedef struct VexNode {//數據域int data;ArcBox* firstIn, *firstOut;//以該節點為弧頭或弧尾的首節點 }VexNode;?在有向圖中每一個頂點與之對應的弧,具體的弧的存儲結構如下所示
其中tailvex表示的是在這一條弧中弧尾節點在頂點數組中對應的下標,headvex表示的是在這一條弧中弧頭節點在頂點數組中對應的下標;(實際上應該還有一個代表權值的元素weight,但這里不做描述)
代碼實現
//邊集(弧集) typedef struct ArcBox {int tailvex, headvex;//弧尾 弧頭對應的頂點下標struct AcrBox* nextIn, *nextOut;//下一個相同尾節點的弧 下一個相同頭節點的弧 }ArcBox;然而在代碼邏輯上,我們還是需要一個抽象結構,也就是可以用一個結構體把頂點集合與弧的集合聯系在一起,構建十字鏈表
#define MAXVEX 200 typedef struct X {//存儲頂點的一維數組VexNode* Xlist[MAXVEX];int numV, numA;//頂點數 弧數 }OLGraph;2.頂點和弧的連接
還是對于這樣一個有向圖,我們以節點V0為例,可以看到與V0有關的弧有四條,分別是:(V0,V1)、(V0,V2)、(V1,V0)、(V3,V0)因為這是一個有向圖,所以我們應該以有向圖的入度和出度的形式去觀察V0節點,很明顯(V0,V1)、(V0,V2)兩條弧表示V0的出度,而(V1,V0)、(V3,V0)表示的是V0的入度
結合上文的描述,我們可以構建出這個有向圖的頂點集合與弧集,我把它們具現化為如下圖表
怎么理解這張圖表呢?左邊的圖表表示的是從V0到V3的頂點的集合,data是V0到V3的值;右邊的離散的表格代表的就是有向圖中一條一條的弧,也就是每個頂點對應的弧的集合
我還是以V0為例,對于V0入度節點是V1和V3,出度節點是V1和V2,現在要根據上文的描述,把V0的頂點集合和與V0有關的弧的集合聯系起來
?對于頂點集合的結構來說,firstOut應該指向V0的第一個出度節點,從邏輯上來說沒有先后排序,但從代碼上的步驟是遍歷V0的所有的鄰接點去判斷,所以這里firstOut應該指向弧(V0,V1),如下圖所示
?
?而在弧集中的結構來說,nextIn表示的是指向下一個相同尾節點的弧,對于弧(V0,V1)和弧(V0,V2)它們都是從V0開始,也就是它們有著同一個尾節點V0,所以弧(V0,V1)的nextIn應該指向的是弧(V0,V2)
對于頂點集合的結構來說,firstIn應該指向V0的第一個入度節點,從邏輯上來說沒有先后排序,但從代碼上的步驟是遍歷V0的所有的鄰接點去判斷,所以這里firstIn應該指向弧(V1,V0),如下圖所示
?
??而在弧集中的結構來說,nextOut表示的是指向下一個相同頭節點的弧,也就是弧頭所指向的都是V0的弧,即弧(V1,V0)的nextOut應該指向弧(V3,V0)
這個時候與頂點V0和其有關所有的弧都聯系上了。其它的節點同理
三、代碼邏輯實現
在代碼邏輯上對上文描述的頂點和弧的連接采用了鏈表中的頭插法,邏輯比較簡單,不清楚的可以在紙上畫一畫就清楚了
(黑線代表原來的連接,紅線或藍色代表頭插以后的連接)
1.出度
①
②
?③
?2.入度
①
②
③
代碼實現
void creatDG(OLGraph* G) {int vi, vj;//輸入有向圖的頂點數和邊數scanf_s("%d%d", &G->numV, &G->numA);//輸入頂點集的數據for(int i = 0; i < G->numV; i++){scanf_s("%d", &G->Xlist[i]->data);G->Xlist[i]->firstIn = NULL;G->Xlist[i]->firstOut = NULL;}//構建十字鏈表for(int i = 0; i < G->numA; i++){//輸入值 查找相對應的下標//這里就當直接輸入下標scanf_s("%d%d", &vi, &vj);//建立弧的節點ArcBox* p = (ArcBox*)malloc(sizeof(ArcBox));p->tailvex = vi;p->tailvex = vj;//頭插法插入新的邊表節點pp->nextIn = G->Xlist[vj]->firstIn;//指向弧頭相同的下一個弧p->nextOut = G->Xlist[vi]->firstOut;//指向弧尾相同的下一個弧G->Xlist[vi]->firstOut = G->Xlist[vj]->firstIn = p; } }總結
?全部代碼
#include<stdio.h> #include<stdlib.h> #define MAXVEX 200 //十字鏈表 //邊集(弧集) typedef struct ArcBox {int tailvex, headvex;//弧尾 弧頭對應的頂點下標struct AcrBox* nextIn, *nextOut;//下一個相同尾節點的弧 下一個相同頭節點的弧 }ArcBox; //頂點集 typedef struct VexNode {//數據域int data;ArcBox* firstIn, *firstOut;//以該節點為弧頭或弧尾的首節點 }VexNode; //構建十字鏈表 typedef struct X {//存儲頂點的一維數組VexNode* Xlist[MAXVEX];int numV, numA;//頂點數 弧數 }OLGraph;void creatDG(OLGraph* G) {int vi, vj;//輸入有向圖的頂點數和邊數scanf_s("%d%d", &G->numV, &G->numA);//輸入頂點集的數據for(int i = 0; i < G->numV; i++){scanf_s("%d", &G->Xlist[i]->data);G->Xlist[i]->firstIn = NULL;G->Xlist[i]->firstOut = NULL;}//構建十字鏈表for(int i = 0; i < G->numA; i++){//輸入值 查找相對應的下標//這里就當直接輸入下標scanf_s("%d%d", &vi, &vj);//建立弧的節點ArcBox* p = (ArcBox*)malloc(sizeof(ArcBox));p->tailvex = vi;p->tailvex = vj;//頭插法插入新的邊表節點pp->nextIn = G->Xlist[vj]->firstIn;//指向弧頭相同的下一個弧p->nextOut = G->Xlist[vi]->firstOut;//指向弧尾相同的下一個弧G->Xlist[vi]->firstOut = G->Xlist[vj]->firstIn = p; } }如果在一個項目中,需要頻繁的對一個圖的邊(弧)增刪改查呢?那么對十字鏈表來說增加或刪除一條邊有可能就會牽一發而動全身呢?是不是操作很麻煩?這就是十字鏈表的缺點,那么又存在什么樣的結構可以解決上述問題呢?我們下一篇文章再解析。
總結
以上是生活随笔為你收集整理的C语言数据结构、十字链表的分析及实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最全面的Android Studio使用
- 下一篇: PPT的字体