邻接表
鄰接表:所謂鄰接表(adjacency list),就是把從同一個頂點發出的邊鏈接在同一個稱為邊鏈表的單鏈表中。邊鏈表的每個結點代表一條邊,稱為邊結點。每個邊結點有2 個域:該邊終點的序號,以及指向下一個邊結點的指針。在鄰接表中,還需要一個用于存儲頂點信息的頂點數組。例如,圖1.19(a)所示的有向圖對應的鄰接表如圖(b)所示。在頂點數組中,每個元素有兩個成員:一個成員用來存儲頂點信息;另一個成員為該頂點的邊鏈表的表頭指針,指向該頂點的邊鏈表。如果沒有從某個頂點發出的邊,則該頂點沒有邊鏈表,因此表頭指針為空,如圖1.19(b)中的頂點G。在該圖中,如果指針為空,則用符號“∧”表示。
圖1.19有向圖的鄰接表
在鄰接表每個頂點的邊鏈表中,各邊結點所表示的邊都是從該頂點發出的邊,因此這種鄰接表也稱為出邊表。采用鄰接表存儲圖時,求頂點的出度很方便,只需要統計每個頂點的邊鏈表中邊結點的個數即可,但在求頂點的入度時就比較麻煩。在圖1.19(b)中,頂點B 的邊鏈表有3 個邊結點,分別表示邊<B, F>、<B, E>和<B, C>,因此頂點B 的出度為3;頂點C 的邊鏈表中只有1 個邊結點,表示邊<C, E>,因此頂點C 的出度為1。如果需要統計各頂點的入度,可以采用逆鄰接表存儲表示圖。所謂逆鄰接表,也稱為入邊表,就是把進入同一個頂點的邊鏈接在同一個邊鏈表中。
圖1.20有向圖的逆鄰接表
例如,圖1.20(a)所示的有向圖對應的逆鄰接表如圖(b)所示。在圖1.20(b)中,頂點B 的邊鏈表有2 個邊結點,分別表示邊<E, B>和<A, B>,因此頂點B 的入度為2;頂點C 的邊鏈表中也有2個邊結點,分別表示邊<D, C>和<B, C>,因此頂點C 的入度也為2。
接下來以有向圖為例介紹鄰接表的實現方法。為了方便求解頂點的出度和入度,在實現時,把出邊表和入邊表同時包含在圖的鄰接表結構中。有向圖的鄰接表用一個結構體LGraph 存儲表示,其中包含3 個成員:頂點數組vertexs,頂點數vexnum 和邊的數目arcnum,其中頂點數組vertexs 中每個元素都是VNode 結構體變量。VNode 結構體變量存儲圖中每個頂點,它包含3 個成員:頂點信息、出邊表的表頭指針和入邊表的表頭指針,其中后面兩個成員都是ArcNode 結構體類型的指針。ArcNode 結構體存儲邊鏈表中的邊結點,它包含2 個成員:邊的另一個鄰接點的序號,以及指向下一個邊結點的指針。
例題:
問題描述:用鄰接表存儲有向圖,并輸出各頂點的出度和入度。
輸入描述:輸入文件中包含多個測試數據,每個測試數據描述了一個無權有向圖。每個測試數據的第一行為兩個正整數n 和m,1≤ n ≤ 100,1≤ m ≤ 500,分別表示該有向圖的頂點數目和邊數,頂點的序號從1 開始計起。接下來有m 行,每行為兩個正整數,用空格隔開,分別表示一條邊的起點和終點。每條邊出現一次且僅一次,圖中不存在自身環和重邊。輸入文件最后一行為0 0,表示輸入數據結束。
輸出描述:
對輸入文件中的每個有向圖,輸出兩行:第1 行為n 個正整數,表示每個頂點的出度;第2行也為n 個正整數,表示每個頂點的入度。每兩個正整數之間用一個空格隔開,每行的最后一個正整數之后沒有空格。
樣例輸入:
4 71 4
2 1
2 2
2 3
2 3
4 2
4 3
0 0
樣例輸出:
1 4 0 2
1 2 3 1
代碼實現:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <set> #include <map> #define INF 99999999 using namespace std; const int maxn=105; struct ArcNode{//邊結點int adjvex;//有向邊的另一個鄰接點的序號ArcNode *nextarc;//指向下一個邊結點的指針 }; struct VNode{//頂點int data;//頂點信息ArcNode *head1;//出邊表的表頭指針ArcNode *head2;//入邊表的表頭指針 }; struct LGraph{//圖的鄰接表存儲結構VNode vertexs[maxn];//頂點數組int vexnum,arcnum;//頂點數和邊數 }LG; void CreateLG(){ArcNode *p;//用來構造邊鏈表的邊結點指針int v1,v2;//LG.vexnum=LG.arcnum=0;//scanf("%d %d",&LG.vexnum,&LG.arcnum);for(int i=0;i<LG.vexnum;i++){//初始化表頭指針為空LG.vertexs[i].head1=LG.vertexs[i].head2=NULL;}for(int i=0;i<LG.arcnum;i++){scanf("%d %d",&v1,&v2);v1--,v2--;p = new ArcNode;//假定有足夠空間p->adjvex = v2;p->nextarc = LG.vertexs[v1].head1;//插入鏈表LG.vertexs[v1].head1=p;p = new ArcNode;p->adjvex = v1;p->nextarc = LG.vertexs[v2].head2;LG.vertexs[v2].head2=p;} } void DeleteLG(){ArcNode *p;//用來指向邊鏈表中各邊結點的指針for(int i=0;i<LG.vexnum;i++){p=LG.vertexs[i].head1;//釋放第i個頂點出邊表各邊節點所占的存儲空間while(p!=NULL){LG.vertexs[i].head1=p->nextarc;delete p;p=LG.vertexs[i].head1;}p=LG.vertexs[i].head2;//釋放第i個頂點入邊表各邊節點所占的存儲空間while(p!=NULL){LG.vertexs[i].head2=p->nextarc;delete p;p=LG.vertexs[i].head2;}} } int main() {int id,od;//頂點的入度跟出度ArcNode *p;//用來遍歷邊鏈表的邊結點指針while(scanf("%d %d",&LG.vexnum,&LG.arcnum)){if(LG.arcnum+LG.vexnum==0)break;CreateLG();for(int i=0;i<LG.vexnum;i++){//統計各頂點出度并輸出od=0;p=LG.vertexs[i].head1;while(p!=NULL){od++;p=p->nextarc;}if(i==0) printf("%d",od);else printf(" %d",od);}printf("\n");for(int i=0;i<LG.vexnum;i++){//統計各頂點入度并輸出id=0;p=LG.vertexs[i].head2;while(p!=NULL){id++;p=p->nextarc;}if(i==0) printf("%d",id);else printf(" %d",id);}printf("\n");DeleteLG();//釋放}return 0; }- 鄰接表的簡單實現:
總結
- 上一篇: 飞行管理计算机在ATA章节,民航ATA章
- 下一篇: 传递函数转化为状态空间 matlab,多