概述
圖的遍歷是指從圖中的任一頂點出發,對圖中的全部頂點訪問一次且只訪問一次。圖的遍歷操做和樹的遍歷操做功能類似。圖的遍歷是圖的一種基本操做,圖的其它算法如求解圖的連通性問題,拓撲排序,求關鍵路徑等都是創建在遍歷算法的基礎之上。node
因爲圖結構自己的複雜性,因此圖的遍歷操做也較複雜,主要表如今如下四個方面:
① 在圖結構中,沒有一個「天然」的首結點,圖中任意一個頂點均可做爲第一個被訪問的結點。
② 在非連通圖中,從一個頂點出發,只可以訪問它所在的連通份量上的全部頂點,所以,還需考慮如何選取下一個出發點以訪問圖中其他的連通份量。
③ 在圖結構中,若是有迴路存在,那麼一個頂點被訪問以後,有可能沿迴路又回到該頂點。ios
④ 在圖結構中,一個頂點能夠和其它多個頂點相連,當這樣的頂點訪問事後,存在如何選取下一個要訪問的頂點的問題。算法
圖的遍歷一般有深度優先搜索和廣度優先搜索兩種方式,他們對無向圖和有向圖都適用。數組
1.深度優先搜索
深度優先搜索(Depth_Fisrst Search)遍歷相似於樹的先根遍歷,是樹的先根遍歷的推廣。
假設初始狀態是圖中全部頂點不曾被訪問,則深度優先搜索可從圖中某個頂點發v 出發,訪問此頂點,而後依次從v 的未被訪問的鄰接點出發深度優先遍歷圖,直至圖中全部和v 有路徑相通的頂點都被訪問到;若此時圖中尚有頂點未被訪問,則另選圖中一個不曾被訪問的頂點做起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。數據結構
以以下圖的無向圖G5爲例,進行圖的深度優先搜索:app

G5ide
搜索過程:函數

假設從頂點v1 出發進行搜索,在訪問了頂點v1 以後,選擇鄰接點v2。由於v2 不曾訪問,則從v2 出發進行搜索。依次類推,接着從v4 、v8 、v5 出發進行搜索。在訪問了v5 以後,因爲v5 的鄰接點都已被訪問,則搜索回到v8。因爲一樣的理由,搜索繼續回到v4,v2 直至v1,此時因爲v1 的另外一個鄰接點未被訪問,則搜索又從v1 到v3,再繼續進行下去由此,獲得的頂點訪問序列爲:ui

顯然,這是一個遞歸的過程。爲了在遍歷過程當中便於區分頂點是否已被訪問,需附設訪問標誌數組visited[0:n-1], ,其初值爲FALSE ,一旦某個頂點被訪問,則其相應的份量置爲TRUE。
1)鄰接矩陣的存儲方式實現:spa
-
- #pragma once
-
- #include "targetver.h"
- #include <stdio.h>
- #include "stdlib.h"
- #include <iostream>
- using namespace std;
-
- #define TRUE 1
- #define FALSE 0
- #define NULL 0
- #define OK 1
- #define ERROR 0
- #define INFEASIBLE -1
- #define OVERFLOW -2
-
- #define INFINITY INT_MAX
- #define MAX_VERTEX_NUM 30
-
-
- typedef int Status ;
- typedef int ElemType ;
- typedef int VrType ;
- typedef char VertexType ;
-
- typedef struct ArcCell{
- VrType adj;
- ArcCell *info;
- }ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
-
- typedef struct{
- VertexType vexs[MAX_VERTEX_NUM];
- AdjMatrix arcs;
- int vexnum,arcnum;
- }MGraph;
- #include "stdafx.h"
-
- bool visited[MAX_VERTEX_NUM];
- Status (*VisitFunc) (int v);
- int LocateVex(MGraph G,VertexType v)
- {
- for(int i = 0; i<G.vexnum; ++i) {
- if(G.vexs[i] == v) return i;
- }
- return -1;
- }
-
- int FirstAdjVex(MGraph G,int v)
- {
- int i ;
- for(i = 0; i<G.vexnum; i++)
- if( G.arcs[v][i].adj ) return i;
- if(i == (G.vexnum -1)) return -1;
- return -1;
-
- }
-
- int NextAdjVex(MGraph G,int v,int w)
- {
- int i;
- for( i = w+1; i<G.vexnum; i++)
- if(G.arcs[v][i].adj) return i;
- if(i == (G.vexnum -1)) return -1;
- return -1;
-
- }
-
- void CreatUDG(MGraph &G){
- cout<<"建立鄰接矩陣的無向圖:"<<endl;
- int i,j,k,w;
-
- G.arcnum = 8;
- G.vexnum = 9;
- for(i=0;i<G.vexnum;++i)
- for(j=0;j<G.vexnum;++j) {
- G.arcs[i][j].adj=0;
- G.arcs[i][j].info=NULL;
- }
- G.vexs[0] = '1';
- G.vexs[1] = '2';
- G.vexs[2] = '3';
- G.vexs[3] = '4';
- G.vexs[4] = '5';
- G.vexs[5] = '6';
- G.vexs[6] = '7';
- G.vexs[7] = '8';
-
- G.arcs[0][1].adj = 1;
- G.arcs[0][1].info = NULL;
- G.arcs[1][0].adj = 1;
- G.arcs[1][0].info = NULL;
-
- G.arcs[1][3].adj = 1;
- G.arcs[1][3].info = NULL;
- G.arcs[3][1].adj = 1;
- G.arcs[3][1].info = NULL;
-
- G.arcs[3][7].adj = 1;
- G.arcs[3][7].info = NULL;
- G.arcs[7][3].adj = 1;
- G.arcs[7][3].info = NULL;
-
- G.arcs[7][4].adj = 1;
- G.arcs[7][4].info = NULL;
- G.arcs[4][7].adj = 1;
- G.arcs[4][7].info = NULL;
-
- G.arcs[4][1].adj = 1;
- G.arcs[4][1].info = NULL;
- G.arcs[1][4].adj = 1;
- G.arcs[1][4].info = NULL;
-
- G.arcs[0][2].adj = 1;
- G.arcs[0][2].info = NULL;
- G.arcs[2][0].adj = 1;
- G.arcs[2][0].info = NULL;
-
- G.arcs[2][5].adj = 1;
- G.arcs[2][5].info = NULL;
- G.arcs[5][2].adj = 1;
- G.arcs[5][2].info = NULL;
-
- G.arcs[5][6].adj = 1;
- G.arcs[5][6].info = NULL;
- G.arcs[6][5].adj = 1;
- G.arcs[6][5].info = NULL;
-
- G.arcs[6][2].adj = 1;
- G.arcs[6][2].info = NULL;
- G.arcs[2][6].adj = 1;
- G.arcs[2][6].info = NULL;
- return ;
-
- }
- void CreatDG(MGraph &G){
- int i,j,k,w;
- char v1,v2;
- G.arcnum = 8;
- G.vexnum = 9;
- cout<<"請輸入有向圖頂點個數和邊數:";
- cin>> G.vexnum>> G.arcnum;
- cout<<"請輸入"<<G.vexnum<<"個頂點的值:"<<endl;
- for(i=0;i<G.vexnum;++i) cin>>G.vexs[i];
- for(i=0;i<G.vexnum;++i)
- for(j=0;j<G.vexnum;++j) {
- G.arcs[i][j].adj = 0;
- G.arcs[i][j].info = NULL;
- }
- for( k=1;k<=G.arcnum;++k){
- cout<<"請輸入第"<<k<<"條邊的兩個頂點值和它們的權重:"<<endl;
- cin>>v1>>v2>>w;
- i= LocateVex(G,v1); j = LocateVex(G,v2);
- G.arcs[i][j].adj = w;
- }
- }
-
-
- void visitVex(MGraph G, int v){
- cout<<G.vexs[v]<<" ";
- }
-
- void DFS(MGraph G,int v){
- visited[v] = true;
- visitVex( G, v);
- for(int w = FirstAdjVex(G,v); w>=0; w = NextAdjVex(G,v,w)){
- if(!visited[w]) DFS(G,w);
-
- }
- }
-
- void DFSTraverse(MGraph G){
- int v;
- for( v = 0; v < G.vexnum; ++v) visited[v] = false;
- for( v = 0; v < G.vexnum; )
- if(!visited[v]) DFS( G, v);
- ++v;
-
- }
-
-
- void printMGraph(MGraph G){
- cout<<"鄰接矩陣已經建立,鄰接矩陣爲:"<<endl;
- for(int i=0;i<G.vexnum;i++){
- for(int j=0;j<G.vexnum;j++)
- cout<<G.arcs[i][j].adj<<" ";
- cout<<endl;
- }
- }
-
-
- void main(){
-
- MGraph G;
-
- CreatUDG(G);
- printMGraph(G);
- cout<<"無向圖鄰接矩陣的深度遍歷結果:"<<endl;
- DFSTraverse(G);
- }
2) 鄰接表的表示實現方式
-
- #pragma once
-
- #include "targetver.h"
- #include <stdio.h>
- #include "stdlib.h"
- #include <iostream>
- using namespace std;
-
- #define TRUE 1
- #define FALSE 0
- #define NULL 0
- #define OK 1
- #define ERROR 0
- #define INFEASIBLE -1
- #define OVERFLOW -2
-
- #define INFINITY INT_MAX
- #define MAX_VERTEX_NUM 30
-
-
- typedef int Status ;
- typedef int ElemType ;
- typedef int VrType ;
- typedef char VertexType ;
-
- typedef struct ArcNode
- {
- int adjvex;
- ArcNode *nextarc;
- }ArcNode;
-
- typedef struct VNode
- {
- VertexType data;
- ArcNode *firstarc;
- }VNode,AdjList[MAX_VERTEX_NUM];
-
- typedef struct
- {
- AdjList vertices;
- int vexnum,arcnum;
- }ALGraph;
- #include "stdafx.h"
-
- bool visited[MAX_VERTEX_NUM];
- Status (*VisitFunc) (int v);
-
-
- void ArcAdd(ALGraph &G,int m,int n){
-
- ArcNode *p,*h,*q;
- p = new ArcNode;
- p->adjvex = m;
- p->nextarc = NULL;
- h = q = G.vertices[n].firstarc;
- if(q == NULL)
- G.vertices[n].firstarc = p;
- else {
- if((p->adjvex)>(q->adjvex)){
- p->nextarc = q;
- G.vertices[n].firstarc = p;
- }
- else {
- while( G.vertices[n].firstarc != NULL && q->nextarc != NULL && (p->adjvex)<(q->adjvex)){
- h = q;
- q = q->nextarc;
- }
- if(q->nextarc == NULL&&(p->adjvex)<(q->adjvex)){
- q->nextarc = p;
- }
- else {
- p->nextarc = q;
- h->nextarc = p;
- }
- }
- }
- }
- void CreateDG(ALGraph &G){
- cout<<"請輸入頂點個數和邊數:"<<endl;
- cin>> G.vexnum>> G.arcnum;
- cout<<"請輸入頂點值:"<<endl;
- for(int i= 1; i<= G.vexnum; i++) {
- char t;
- cin>>t;
- G.vertices[i].data = t;
- G.vertices[i].firstarc = NULL;
- }
- int m, n;
- for(int k = 1; k<=G.arcnum; k++){
- cout<<"請輸入第"<<k<<"條邊的兩個頂點:"<<endl;
- cin>>m>>n;
- if(m<= G.vexnum && n <= G.vexnum && m>0 && n>0){
- ArcAdd(G, m, n);
- ArcAdd(G, n, m);
- }
- else cout<<"ERROR."<<endl;
- }
- }
- void PrintGraph(ALGraph G)
- {
- cout<<"無向圖的建立完成,該圖的鄰接表表示爲:"<<endl;
- ArcNode *p;
- for(int i=1; i<=G.vexnum; i++)
- {
- if(G.vertices[i].firstarc == NULL)
- cout<<i<<G.vertices[i].data<<"-->NULL"<<endl;
- else
- {
- p = G.vertices[i].firstarc;
- cout<<i<<G.vertices[i].data<<"-->";
- while(p->nextarc!=NULL)
- {
- cout<<p->adjvex<<"-->";
- p = p->nextarc;
- }
- cout<<p->adjvex<<"-->NULL"<<endl;
- }
- }
- }
-
-
- int FirstAdjVex(ALGraph G,int v)
- {
- if(G.vertices[v].firstarc)
- return G.vertices[v].firstarc->adjvex;
- else
- return NULL;
- }
- int NextAdjVex(ALGraph G,int v,int w)
- {
- ArcNode *p;
- if(G.vertices[v].firstarc==NULL)
- return NULL;
- else {
- p = G.vertices[v].firstarc;
- while(p->adjvex!=w) p = p->nextarc;
-
- if(p->nextarc == NULL) return NULL;
- else return p->nextarc->adjvex;
- }
- }
-
-
-
- void visitVex(ALGraph G, int v){
- cout<<G.vertices[v].data<<" ";
- }
-
- void DFS(ALGraph G,int v)
- {
- visited[v] = true;
- visitVex(G, v);
- for(int w = FirstAdjVex(G,v);w >= 1; w = NextAdjVex(G,v,w))
- if(!visited[w]) DFS(G,w);
- }
- void DFSTraverse(ALGraph G)
- {
- for(int v = 1; v <= G.vexnum; v++) visited[v]=false;
- for(int m = 1; m <= G.vexnum; m++)
- if(!visited[m]) DFS(G,m);
- }
-
- void main(){
- ALGraph G;
- CreateDG(G);
- PrintGraph(G);
- DFSTraverse(G);
- }
分析上述算法,在遍歷時,對圖中每一個頂點至多調用一次DFS 函數,由於一旦某個頂點被標誌成已被訪問,就再也不從它出發進行搜索。所以,遍歷圖的過程實質上是對每一個頂點查找其鄰接點的過程。其耗費的時間則取決於所採用的存儲結構。當用二維數組表示鄰接矩陣圖的存儲結構時,查找每一個頂點的鄰接點所需時間爲O(n2) ,其中n 爲圖中頂點數。而當以鄰接表做圖的存儲結構時,找鄰接點所需時間爲O(e),其中e 爲無向圖中邊的數或有向圖中弧的數。由此,當以鄰接表做存儲結構時,深度優先搜索遍歷圖的時間複雜度爲O(n+e) 。
2.廣度優先搜索
廣度優先搜索(Breadth_First Search) 遍歷相似於樹的按層次遍歷的過程。
假設從圖中某頂點v 出發,在訪問了v 以後依次訪問v 的各個不曾訪問過和鄰接點,而後分別從這些鄰接點出發依次訪問它們的鄰接點,並使「先被訪問的頂點的鄰接點」先於「後被訪問的頂點的鄰接點」被訪問,直至圖中全部已被訪問的頂點的鄰接點都被訪問到。若此時圖中尚有頂點未被訪問,則另選圖中一個不曾被訪問的頂點做起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。換句話說,廣度優先搜索遍歷圖的過程當中以v 爲起始點,由近至遠,依次訪問和v 有路徑相通且路徑長度爲1,2,…的頂點。
對圖以下圖所示無向圖G5 進行廣度優先搜索遍歷:

廣度搜索過程:

首先訪問v1 和v1 的鄰接點v2 和v3,而後依次訪問v2 的鄰接點v4 和v5 及v3 的鄰接點v6 和v7,最後訪問v4 的鄰接點v8。因爲這些頂點的鄰接點均已被訪問,而且圖中全部頂點都被訪問,由些完成了圖的遍歷。獲得的頂點訪問序列爲:
v1→v2 →v3 →v4→ v5→ v6→ v7 →v8
和深度優先搜索相似,在遍歷的過程當中也須要一個訪問標誌數組。而且,爲了順次訪問路徑長度爲二、三、…的頂點,需附設隊列以存儲已被訪問的路徑長度爲一、二、… 的頂點。
實現:
-
- #pragma once
-
- #include <stdio.h>
- #include "stdlib.h"
- #include "stdafx.h"
- #include "func.h"
- bool visited[MAX_VERTEX_NUM];
- Status (*VisitFunc) (int v);
-
- void ArcAdd(ALGraph &G,int m,int n){
-
- ArcNode *p,*h,*q;
- p = new ArcNode;
- p->adjvex = m;
- p->nextarc = NULL;
- h = q = G.vertices[n].firstarc;
- if(q == NULL)
- G.vertices[n].firstarc = p;
- else {
- if((p->adjvex)>(q->adjvex)){
- p->nextarc = q;
- G.vertices[n].firstarc = p;
- }
- else {
- while( G.vertices[n].firstarc != NULL && q->nextarc != NULL && (p->adjvex)<(q->adjvex)){
-
- h = q;
- q = q->nextarc;
- }
- if(q->nextarc == NULL&&(p->adjvex)<(q->adjvex)){
- q->nextarc = p;
- }
- else {
- p->nextarc = q;
- h->nextarc = p;
- }
- }
- }
- }
- void CreateDG(ALGraph &G){
- cout<<"請輸入頂點個數和邊數:"<<endl;
- cin>> G.vexnum>> G.arcnum;
- cout<<"請輸入頂點值:"<<endl;
- for(int i= 1; i<= G.vexnum; i++) {
- char t;
- cin>>t;
- G.vertices[i].data = t;
- G.vertices[i].firstarc = NULL;
- }
- int m, n;
- for(int k = 1; k<=G.arcnum; k++){
- cout<<"請輸入第"<<k<<"條邊的兩個頂點:"<<endl;
- cin>>m>>n;
- if(m<= G.vexnum && n <= G.vexnum && m>0 && n>0){
- ArcAdd(G, m, n);
- ArcAdd(G, n, m);
- }
- else cout<<"ERROR."<<endl;
- }
- }
-
- void PrintGraph(ALGraph G)
- {
- cout<<"無向圖的建立完成,該圖的鄰接表表示爲:"<<endl;
- ArcNode *p;
- for(int i=1; i<=G.vexnum; i++)
- {
- if(G.vertices[i].firstarc == NULL)
- cout<<i<<G.vertices[i].data<<"-->NULL"<<endl;
- else
- {
- p = G.vertices[i].firstarc;
- cout<<i<<G.vertices[i].data<<"-->";
- while(p->nextarc!=NULL)
- {
- cout<<p->adjvex<<"-->";
- p = p->nextarc;
- }
- cout<<p->adjvex<<"-->NULL"<<endl;
- }
- }
- }
-
- int FirstAdjVex(ALGraph G,int v)
- {
- if(G.vertices[v].firstarc)
- return G.vertices[v].firstarc->adjvex;
- else
- return NULL;
- }
- int NextAdjVex(ALGraph G,int v,int w)
- {
- ArcNode *p;
- if(G.vertices[v].firstarc==NULL)
- return NULL;
- else {
- p = G.vertices[v].firstarc;
- while(p->adjvex!=w) p = p->nextarc;
-
- if(p->nextarc == NULL) return NULL;
- else return p->nextarc->adjvex;
- }
- }
-
- void visitVex(ALGraph G, int v){
- cout<<G.vertices[v].data<<" ";
- }
-
-
- void BFSTraverse(ALGraph G)
- {
- Queue Q;
- int u;
- for(int m=1; m<= G.vexnum; m++) visited[m] = false;
- InitQueue(Q);
- for(int v=1;v<=G.vexnum;v++)
- if(!visited[v]) {
- visited[v]=true;
- visitVex(G,v);
- EnQueue(Q,v);
- while(Q.len!=0)
- {
- DeleteQueue(Q,u);
- for(int w=FirstAdjVex(G,u);w>=1;w=NextAdjVex(G,u,w))
- if(!visited[w])
- {
- visited[w]=true;
- visitVex(G,v);
- EnQueue(Q,w);
- }
- }
- }
- cout<<endl;
- }
-
- void main(){
- ALGraph G;
- CreateDG(G);
- PrintGraph(G);
- cout<<"廣度優先搜索的結果爲:"<<endl;
- BFSTraverse(G);
- }
分析上述算法,每一個頂點至多進一次隊列。遍歷圖的過程實質是經過邊或弧找鄰接點的過程,所以廣度優先搜索遍歷圖的時間複雜度和深度優先搜索遍歷相同,二者不一樣之處僅僅在於對頂點訪問的順序不一樣。
轉自:http://blog.csdn.net/hguisu/article/details/7712813