鄰接矩陣是一種不錯的圖存儲結構,但是我們發現,對於邊數相對較少的圖,這種結構是存在對存儲空間的極大浪費的。我們知道,順序存儲結構存在預先分配內存可能造成空間浪費的問題,於是引出了鏈式存儲的結構。同樣的,我們也可以考慮對邊或弧使用鏈式存儲的方式來避免空間浪費的問題。因此,對於圖的存儲結構,我們同樣引入了一種數組與鏈表相組合的存儲方法,我們一般稱之為鄰接表。
鄰接表的處理方法是這樣的:(1).圖中頂點用一個一維數組存儲,當然,頂點也可以用單鏈表來存儲,不過數組可以較容易的讀取頂點的信息,更加方便;另外,每個數據元素還需要存儲指向第一個鄰接點的指針,以便於查找該頂點的邊信息;(2).圖中每個頂點Vi的所有鄰接點構成一個線性表,由於鄰接點的個數不定,所以用單鏈表存儲,無向圖稱為頂點Vi的邊表,有向圖則稱為頂點Vi作為弧尾的出邊表。
對比圖的深度優先遍歷與廣度優先遍歷,我們發現,它們在時間復雜度上是一樣的,不同之處僅僅在於對頂點的訪問順序不同,可見兩者在全圖遍歷上是沒有優劣之分的,只是視不同的情況選擇不同的方式而已。不過深度優先遍歷更適合於目標比較明確,以找到目標為主要目的的情況,而廣度優先遍歷更適合在不斷擴大遍歷范圍時找到相對最優解的情況。
圖的創建及鄰接表的深度和廣度優先遍歷源程序代碼如下所示:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define OK 1 5 #define ERROR 0 6 #define TRUE 1 7 #define FALSE 0 8 9 #define MAXSIZE 9 /* 存儲空間初始分配量 */ 10 #define MAXEDGE 15 11 #define MAXVEX 9 12 #define INFINITY 65535 13 14 typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ 15 typedef int Boolean; /* Boolean是布爾類型,其值是TRUE或FALSE */ 16 17 typedef char VertexType; /* 頂點類型應由用戶定義 */ 18 typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */ 19 20 /* 鄰接矩陣結構 */ 21 typedef struct 22 { 23 VertexType vexs[MAXVEX]; /* 頂點表 */ 24 EdgeType arc[MAXVEX][MAXVEX]; /* 鄰接矩陣,可看作邊表 */ 25 int numVertexes, numEdges; /* 圖中當前的頂點數和邊數 */ 26 }MGraph; 27 28 /* 鄰接表結構 */ 29 typedef struct EdgeNode /* 邊表結點 */ 30 { 31 int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */ 32 int weight; /* 用於存儲權值,對於非網圖可以不需要 */ 33 struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */ 34 }EdgeNode; 35 36 typedef struct VertexNode /* 頂點表結點 */ 37 { 38 int in; /* 頂點入度 */ 39 char data; /* 頂點域,存儲頂點信息 */ 40 EdgeNode *firstedge; /* 邊表頭指針 */ 41 }VertexNode, AdjList[MAXVEX]; 42 43 typedef struct 44 { 45 AdjList adjList; 46 int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */ 47 }graphAdjList,*GraphAdjList; 48 49 /* 用到的隊列結構與函數 */ 50 /* 循環隊列的順序存儲結構 */ 51 typedef struct 52 { 53 int data[MAXSIZE]; 54 int front; /* 頭指針 */ 55 int rear; /* 尾指針,若隊列不空,指向隊列尾元素的下一個位置 */ 56 }Queue; 57 58 /* 初始化一個空隊列Q */ 59 Status InitQueue(Queue *Q) 60 { 61 Q->front=0; 62 Q->rear=0; 63 return OK; 64 } 65 66 /* 若隊列Q為空隊列,則返回TRUE,否則返回FALSE */ 67 Status QueueEmpty(Queue Q) 68 { 69 if(Q.front==Q.rear) /* 隊列空的標志 */ 70 return TRUE; 71 else 72 return FALSE; 73 } 74 75 /* 若隊列未滿,則插入元素e為Q新的隊尾元素 */ 76 Status EnQueue(Queue *Q,int e) 77 { 78 if ((Q->rear+1)%MAXSIZE == Q->front) /* 隊列滿的判斷 */ 79 return ERROR; 80 Q->data[Q->rear]=e; /* 將元素e賦值給隊尾 */ 81 Q->rear=(Q->rear+1)%MAXSIZE; /* rear指針向后移一位置, */ 82 /* 若到最后則轉到數組頭部 */ 83 return OK; 84 } 85 86 /* 若隊列不空,則刪除Q中隊頭元素,用e返回其值 */ 87 Status DeQueue(Queue *Q,int *e) 88 { 89 if (Q->front == Q->rear) /* 隊列空的判斷 */ 90 return ERROR; 91 *e=Q->data[Q->front]; /* 將隊頭元素賦值給e */ 92 Q->front=(Q->front+1)%MAXSIZE; /* front指針向后移一位置, */ 93 /* 若到最后則轉到數組頭部 */ 94 return OK; 95 } 96 97 void CreateMGraph(MGraph *G) 98 { 99 int i, j; 100 101 G->numEdges=15; 102 G->numVertexes=9; 103 104 /* 讀入頂點信息,建立頂點表 */ 105 G->vexs[0]='A'; 106 G->vexs[1]='B'; 107 G->vexs[2]='C'; 108 G->vexs[3]='D'; 109 G->vexs[4]='E'; 110 G->vexs[5]='F'; 111 G->vexs[6]='G'; 112 G->vexs[7]='H'; 113 G->vexs[8]='I'; 114 115 for (i = 0; i < G->numVertexes; i++) /* 初始化圖 */ 116 { 117 for ( j = 0; j < G->numVertexes; j++) 118 { 119 G->arc[i][j]=0; 120 } 121 } 122 123 G->arc[0][1]=1; 124 G->arc[0][5]=1; 125 126 G->arc[1][2]=1; 127 G->arc[1][8]=1; 128 G->arc[1][6]=1; 129 130 G->arc[2][3]=1; 131 G->arc[2][8]=1; 132 133 G->arc[3][4]=1; 134 G->arc[3][7]=1; 135 G->arc[3][6]=1; 136 G->arc[3][8]=1; 137 138 G->arc[4][5]=1; 139 G->arc[4][7]=1; 140 141 G->arc[5][6]=1; 142 143 G->arc[6][7]=1; 144 145 for(i = 0; i < G->numVertexes; i++) 146 { 147 for(j = i; j < G->numVertexes; j++) 148 { 149 G->arc[j][i] =G->arc[i][j]; 150 } 151 } 152 } 153 154 /* 利用鄰接矩陣構建鄰接表 */ 155 void CreateALGraph(MGraph G,GraphAdjList *GL) 156 { 157 int i,j; 158 EdgeNode *e; 159 160 *GL = (GraphAdjList)malloc(sizeof(graphAdjList)); 161 162 (*GL)->numVertexes=G.numVertexes; 163 (*GL)->numEdges=G.numEdges; 164 for(i= 0;i <G.numVertexes;i++) /* 讀入頂點信息,建立頂點表 */ 165 { 166 (*GL)->adjList[i].in=0; 167 (*GL)->adjList[i].data=G.vexs[i]; 168 (*GL)->adjList[i].firstedge=NULL; /* 將邊表置為空表 */ 169 } 170 171 for(i=0;i<G.numVertexes;i++) /* 建立邊表 */ 172 { 173 for(j=0;j<G.numVertexes;j++) 174 { 175 if (G.arc[i][j]==1) 176 { 177 e=(EdgeNode *)malloc(sizeof(EdgeNode)); 178 e->adjvex=j; /* 鄰接序號為j */ 179 e->next=(*GL)->adjList[i].firstedge; /* 將當前頂點上的指向的結點指針賦值給e */ 180 (*GL)->adjList[i].firstedge=e; /* 將當前頂點的指針指向e */ 181 (*GL)->adjList[j].in++; 182 183 } 184 } 185 } 186 } 187 188 Boolean visited[MAXSIZE]; /* 訪問標志的數組 */ 189 190 /* 鄰接表的深度優先遞歸算法 */ 191 void DFS(GraphAdjList GL, int i) 192 { 193 EdgeNode *p; 194 visited[i] = TRUE; 195 printf("%c ",GL->adjList[i].data); /* 打印頂點,也可以其它操作 */ 196 p = GL->adjList[i].firstedge; 197 while(p) 198 { 199 if(!visited[p->adjvex]) 200 DFS(GL, p->adjvex); /* 對為訪問的鄰接頂點遞歸調用 */ 201 p = p->next; 202 } 203 } 204 205 /* 鄰接表的深度遍歷操作 */ 206 void DFSTraverse(GraphAdjList GL) 207 { 208 int i; 209 for(i = 0; i < GL->numVertexes; i++) 210 visited[i] = FALSE; /* 初始所有頂點狀態都是未訪問過狀態 */ 211 for(i = 0; i < GL->numVertexes; i++) 212 if(!visited[i]) /* 對未訪問過的頂點調用DFS,若是連通圖,只會執行一次 */ 213 DFS(GL, i); 214 } 215 216 /* 鄰接表的廣度遍歷算法 */ 217 void BFSTraverse(GraphAdjList GL) 218 { 219 int i; 220 EdgeNode *p; 221 Queue Q; 222 for(i = 0; i < GL->numVertexes; i++) 223 visited[i] = FALSE; 224 InitQueue(&Q); 225 for(i = 0; i < GL->numVertexes; i++) 226 { 227 if (!visited[i]) 228 { 229 visited[i]=TRUE; 230 printf("%c ",GL->adjList[i].data); /* 打印頂點,也可以其它操作 */ 231 EnQueue(&Q,i); 232 while(!QueueEmpty(Q)) 233 { 234 DeQueue(&Q,&i); 235 p = GL->adjList[i].firstedge; /* 找到當前頂點的邊表鏈表頭指針 */ 236 while(p) 237 { 238 if(!visited[p->adjvex]) /* 若此頂點未被訪問 */ 239 { 240 visited[p->adjvex]=TRUE; 241 printf("%c ",GL->adjList[p->adjvex].data); 242 EnQueue(&Q,p->adjvex); /* 將此頂點入隊列 */ 243 } 244 p = p->next; /* 指針指向下一個鄰接點 */ 245 } 246 } 247 } 248 } 249 } 250 251 252 int main(void) 253 { 254 MGraph G; 255 GraphAdjList GL; 256 CreateMGraph(&G); 257 CreateALGraph(G,&GL); 258 259 printf("\n1.圖的鄰接表的深度優先遍歷為:"); 260 DFSTraverse(GL); 261 262 printf("\n2.圖的鄰接表的廣度優先遍歷為:"); 263 BFSTraverse(GL); 264 265 return 0; 266 }
