一.鄰接矩陣存儲方法
鄰接矩陣是表示頂點之間相鄰關系的矩陣。設G=(V,E)是具有n個頂點的圖,頂點的順序依次是(v0,v1,v2,.....vn-1),則G的鄰接矩陣A是n階方陣:
若A是無向圖,A[i][j]=1,表示i,j之間有一條邊,i到j可達且j到i可達。若A是無向圖,A[i][j]=1,表示i到j可達,j到i不可達。
鄰接矩陣的特點如下:
(1)圖的鄰接矩陣表示是唯一的;
(2)無向圖的鄰接矩陣是對稱矩陣。壓縮方法:存儲時只需存儲上三角或下三角;
(3)當鄰接矩陣是稀疏矩陣時,當圖中頂點較多時,可以采用三元組表的方法存儲;
(4)對於無向圖,鄰接矩陣的第i行(或第i列)非零元素的個數正好是第i個頂點的度。
對於有向圖,第i行(第i列)非零元素的個數正好是第i個頂點的出度(入度);
局限性:要確定圖中有多少條邊,必須按行,按列對每個元素進行檢測,花費時間代價很大;
1 #define M 最大頂點個數 2 typedef struct 3 { 4 int no;//頂點編號 5 ...//其他信息 6 }vertextype; 7 typedef struct//定義圖 8 { 9 int edge[M][M];//鄰接矩陣 10 int n,e;//頂點數,弧數 11 vertextype vexs[M];//存放頂點信息 12 }Mgraph;
例如:要存儲10個點直接的關系,可以用一個10*10的鄰接矩陣來存儲,代碼如下:
1 #include<stdio.h> 2 #include<string.h> 3 #define M 11 4 typedef struct 5 { 6 int no; 7 }vertextype; 8 typedef struct 9 { 10 int edge[M][M]; 11 int n,e; 12 vertextype vexs; 13 }Mgraph; 14 int main() 15 { 16 Mgraph A; 17 int a,b; 18 memset(A.edge,0,sizeof(A.edge)); 19 A.n=10; 20 scanf("%d",&A.e); 21 for(int i=1;i<=A.e;i++) 22 { 23 scanf("%d%d",&a,&b); 24 A.edge[a][b]=1; 25 } 26 for(int i=1;i<=10;i++) 27 { 28 for(int j=1;j<=10;j++) 29 { 30 printf("%d ",A.edge[i][j]); 31 } 32 printf("\n"); 33 } 34 return 0; 35 }
二.鄰接表存儲方法
在我們平時使用鄰接矩陣的時候經常會遇到以下三種情況:
1. 對於一個|V|很大的圖,無法使用鄰接矩陣進行存儲。對於256M的內存,所以我們最多能使用的|V| = 8192的鄰接矩陣。
2. 對於某些稀疏圖或者無根樹,每個節點都擁有大小為|V|的數組,但實際使用到的只有很小一部分,造成了很多空間上的浪費。
3. 有時兩個點之間不止存在有一條邊,這是用鄰接矩陣就無法同時表示兩條以上的邊。
由以上情況我們想到了一種特殊的圖存儲方式,讓每個節點擁有的數組大小剛好就等於它所連接的邊數。
鄰接表存儲方法是一種鏈式分配和鏈式分配相結合的存儲方法。在鄰接表中,對圖中的每個頂點建立一個單鏈表,第i個單鏈表中的結點依附於頂點vi的邊(對有向圖是以頂點vi為尾的弧)。每個單鏈表上附設一個表頭結點。
表結點由三個域組成,adjvex存儲與vi鄰接的點在圖中的位置,nextarc存儲下一條邊或弧的結點,data存儲與邊或弧相關的信息如權值。;表頭結點由兩個域組成,分別指向鏈表中第一個頂點和存儲vi的名或其他信息。
鄰接表的特點如下:
(1)表示不唯一;
(2)對於稀疏圖,鄰接表比鄰接矩陣更省空間;
(3)對於無向圖,vi對應第i個鏈表的表結點個數等於頂點得度;
對於有向圖,vi對應第i個鏈表的表結點個數等於頂點的出度,其入度為鄰接表中所有adjvex值域為i的邊結點數目。
1 typedef struct ANode//弧的結點結構類型 2 { 3 int adjvex;//弧的終點位置 4 sgruct ANode *nextarc;//指向下一條弧的指針 5 }ArcNode; 6 typedef struct VNode//鄰接表頭結點類型 7 { 8 Vertex data;//頂點信息 9 ArcNode *firstarc;//指向第一條弧 10 }Vnode; 11 typedef Vnode A[M];//A是鄰接表類型 12 typedef struct 13 { 14 A adjlist;//鄰接表 15 int n,e; 16 }Agraph;//圖的類型
逆鄰接表:就是在有向圖的鄰接表中,對每個頂點,連接的是指向該頂點的弧。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #define MaxVertexNum 100 4 typedef char VertexType; 5 typedef struct node //邊表節點 6 { 7 int adjvex; 8 node* next; 9 } EdgeNode; 10 typedef struct //頂點表節點 11 { 12 VertexType vertex; 13 EdgeNode* firstedge; 14 } VertexNode; 15 typedef VertexNode AdjList[MaxVertexNum]; 16 typedef struct 17 { 18 AdjList adjlist; 19 int n,e; 20 21 } ALGraph; 22 void create(ALGraph*); 23 void main() 24 { 25 ALGraph* G= (ALGraph*)malloc(sizeof(ALGraph)); 26 create(G); 27 for (int i=0; i< G->n; i++) 28 { 29 printf("%d->",i); 30 while(G->adjlist[i].firstedge!=NULL) 31 { 32 printf("%d->",G->adjlist[i].firstedge->adjvex); 33 G->adjlist[i].firstedge=G->adjlist[i].firstedge->next; 34 35 } 36 printf("\n"); 37 } 38 } 39 void create(ALGraph* G) 40 { 41 int i,j,k,w,v; 42 EdgeNode *s; 43 printf("讀入頂點數和邊數"); 44 scanf("%d,%d",&G->n,&G->e); 45 for (i=0; i<G->n; i++) 46 { 47 fflush(stdin); 48 printf("建立頂點表"); 49 G->adjlist[i].vertex=getchar(); 50 G->adjlist[i].firstedge=NULL; 51 } 52 printf("建立邊表\n"); 53 for (k=0; k<G->e; k++) 54 { 55 printf("讀入(vi-vj)的頂點對序號"); 56 scanf("%d,%d",&i,&j); 57 s=(EdgeNode*)malloc(sizeof(EdgeNode)); 58 s->adjvex=j; 59 s->next=G->adjlist[i].firstedge; //插入表頭 60 G->adjlist[i].firstedge=s; 61 s=(EdgeNode*)malloc(sizeof(EdgeNode)); 62 s->adjvex=i; 63 s->next=G->adjlist[j].firstedge; 64 G->adjlist[j].firstedge=s; 65 66 } 67 }
三. 十字鏈接表存儲方法
實際上是鄰接表和逆鄰接表的結合。在十字鄰接表中,每個邊結點對應圖中的一條邊,把每一條邊的邊結點分別組織到以起始頂點為頭結點的的鏈表和以終點結點為頭結點的鏈表中。
四. 鄰接多重表存儲方法
鄰接多重表(Adjacency Multilist)是無向圖的另一中鏈式存儲結構。雖然鄰接表是無向圖的一種有效的存儲結構,在鄰接表中容易求頂點和邊的各種信息。但是,在鄰接表中的每一條邊的兩個結點分別在不同的鏈表中。這給某些圖的操作帶來不便。(如對搜索過的邊標記或刪除一條邊等)鄰接多重表的結構和十字鏈表相似。給每一條邊用一個結點表示!
在鄰接多重表中,所有依附於同一頂點的邊串聯在同一鏈表中,由於每條邊依附於兩個頂點,則每個邊結點同時鏈接在兩個鏈表中。
可見,對無向圖而言,其鄰接多重表和鄰接表的差別,僅僅在於同一條邊在鄰接表中用兩個結點表示,而在鄰接多重表中只有一個結點。
因此,除了在邊結點增加一個標志域外,鄰接多重表所需的存儲量和鄰接表相同。因此,各種基本操作的實現亦和鄰接表相似。
第三第四種不常用,此處暫不做深入探討。
各位晚安~~
