淺談數據結構之圖的鄰接表深度和廣度優先遍歷(九)


  鄰接矩陣是一種不錯的圖存儲結構,但是我們發現,對於邊數相對較少的圖,這種結構是存在對存儲空間的極大浪費的。我們知道,順序存儲結構存在預先分配內存可能造成空間浪費的問題,於是引出了鏈式存儲的結構。同樣的,我們也可以考慮對邊或弧使用鏈式存儲的方式來避免空間浪費的問題。因此,對於圖的存儲結構,我們同樣引入了一種數組與鏈表相組合的存儲方法,我們一般稱之為鄰接表。

  鄰接表的處理方法是這樣的:(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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM