數據結構與算法10—圖的遍歷
圖的遍歷
1. 在圖中有回路,從圖中某一頂點出發訪問圖中其它頂點時,可能又會回到出發點,而圖中可能還剩余有頂點沒有訪問到。
2. 我們可以設置一個全局型標誌數組visited來標誌某個頂點是否被訪問過,未訪問的值為0,訪問過的值為1。
3. 圖的遍歷有兩種方法:深度優先搜索遍歷(DFS)、廣度優先搜索遍歷(BFS)。
深度優先搜索
深度優先搜索思想
- 首先訪問頂點i,並將其訪問標記置為訪問過,即visited[i] =1;
- 然後搜索與頂點i有邊相連的下一個頂點j,若j未被訪問過,則訪問它,並將j的訪問標記置為訪問過,visited[j]=1,然後從j開始重復此過程,若j已訪問,再看與i有邊相連的其它頂點;
- 若與i有邊相連的頂點都被訪問過,則退回到前一個訪問頂點並重復剛才過程,直到圖中所有頂點都被訪問完為止。
例如:
對下圖所示無向圖G7,從頂點1出發的深度優先搜索遍歷序列可有多種,下面僅給出三種,其它可作類似分析。
1, 2, 4, 8, 5, 6, 3, 7
1, 2, 5, 8, 4, 7, 3, 6
1, 3, 6, 8, 7, 4, 2, 5
可以看出,從某一個頂點出發的遍歷結果是不唯一的。但是,若我們給定圖的存貯結構,則從某一頂點出發的遍歷結果應是唯一的。
1. 用鄰接矩陣實現圖的深度優先搜索
#define MAX_VERTEX_NUM 100 typedefint VertexType; /*假設圖中結點的數據類型為整型*/ typedef struct { VertexType v[MAX_VERTEX_NUM]; /*頂點表*/ int A[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*鄰接矩陣*/ int vexnum,arcnum; /*圖的頂點數和弧數*/ }MGraph; int visited[MAX_VERTEX_NUM]; void DFSM(MGraph G, int i) { printf("%5d",G.v[i]); /*訪問頂點i*/ visited[i]=1; /*標記頂點i已訪問*/ for(int j=0;j<G.vexnum;j++) { if (!visited[j] && G.A[i][j]==1) DFSM(G,j); /*若某個鄰接頂點尚未訪問,則遞歸訪問它*/ } }
用上述算法和無向圖G7,可以描述從頂點1出發的深度優先搜索遍歷過程,其中實線表示下一層遞歸調用,虛線表示遞歸調用的返回。 可以得到從頂點1的遍歷結果為 1, 2, 4, 8, 5, 6, 3, 7。同樣可以分析出從其它頂點出發的遍歷結果。
2. 用鄰接表實現圖的深度優先搜索
圖的鄰接表存儲結構定義:
typedef struct ArcNode { int adjvex; /*該弧所指向的頂點的位置*/ struct ArcNode *nextarc; /*下一條弧*/ InfoType info; /*該弧上的權值*/ } ArcNode; typedef int VertexType; typedef struct VNode { VertexType data; ArcNode *firstarc; }VNode; typedef struct { VNode vertices[MAXV]; int vexnum, arcnum; } ALGraph;
鄰接表存儲時的深度優先搜索算法:
int visited[MAX_VERTEX_NUM]; void DFS(ALGraph G, int v) { ArcNode *p; printf("%5d",G.vertices[v].data); /*訪問頂點v*/ visited[v]=1; /*標記頂點v已訪問*/ p=G.vertices[v].firstarc; /*取第一個鄰接邊*/ while (p) { if (!visited[p->adjvex]) DFS(G,p->adjvex); /*遞歸訪問*/ p=p->nextarc; /*找頂點v的下一個鄰接點*/ } }
用剛才算法,可以描述從頂點7出發的深度優先搜索遍歷示意圖,其中實線表示下一層遞歸,虛線表示遞歸返回,箭頭旁邊數字表示調用的步驟。遍歷序列為 7, 3, 1, 2, 4, 8, 5, 6。
廣度優先搜索
廣度優先搜索的思想
- 首先訪問頂點i,並將其訪問標誌置為已被訪問,即visited[i]=1;
- 接著依次訪問與頂點i有邊相連的所有頂點W1,W2,…,Wt;
- 然後再按順序訪問與W1,W2,…,Wt有邊相連又未曾訪問過的頂點;
- 依此類推,直到圖中所有頂點都被訪問完為止 。
在無向圖G7中,從頂點1出發的廣度優先搜索遍歷序列舉三種為:
1, 2, 3, 4, 5, 6, 7, 8
1, 3, 2, 7, 6, 5, 4, 8
1, 2, 3, 5, 4, 7, 6, 8
1. 用鄰接矩陣實現圖的廣度優先搜索遍歷
根據該算法用及圖7-14中的鄰接矩陣,可以得到圖G7的廣度優先搜索序列,
若從頂點1 出發,廣度優先搜索序列為:1,2,3, 4,5, 6,7,8。
若從頂點3出發,廣度優先搜索序列為:3, 1, 6, 7, 2, 8, 4, 5。
鄰接矩陣存儲時的寬度優先搜索算法:
void BFSM(MGraph G,int i) { int j; Queue Q; InitQueue(&Q); InQueue(&Q,i); printf("%5d",G.v[i]); /*訪問初始頂點v*/ visited[i]=1; /*置已訪問標記*/ while(OutQueue(&Q,&i)) { /*若隊列不空時循環*/ for(j=0;j<G.vexnum;j++) { if (!visited[j] && G.A[i][j]==1) { visited[j]=1; printf("%5d",G.v[j]); /*訪問v*/ InQueue(&Q,j); /*該頂點進隊*/ } } } }
2. 用鄰接表實現圖的廣序優先搜索遍歷
可以得到圖G7的廣度優先搜索序列,
若從頂點1出發,廣度優先搜索序列為:1,2,3,4,5,6,7,8,
若從頂點7出發,廣度優先搜索序列為:7,3,8,1,6,4,5,2。
鄰接表存儲時的寬度優先搜索算法:
void BFS(ALGraph G,int v) { ArcNode *p; int x; Queue Q; InitQueue(&Q); InQueue(&Q,v); printf("%5d",G.vertices[v].data); /*訪問初始頂點v*/ visited[v]=1; /*置已訪問標記*/ while(OutQueue(&Q,&x)) { /*若隊列不空時循環*/ p=G.vertices[x].firstarc; /*與x鄰接的第一個頂點*/ while(p!=NULL) { if (visited[p->adjvex]==0) { /*若未被訪問*/ visited[p->adjvex]=1; printf("%5d",G.vertices[p->adjvex].data); InQueue(&Q,p->adjvex); /*該頂點進隊*/ } p=p->nextarc; /*找下一個鄰接點*/ } } }
數據結構與算法10—圖的遍歷