DFS 深度優先遍歷
DFS算法用於遍歷圖結構,旨在遍歷每一個結點,顧名思義,這種方法把遍歷的重點放在深度上,什么意思呢?就是在訪問過的結點做標記的前提下,一條路走到天黑,我們都知道當每一個結點都有很多分支,那么我們的小人就沿着每一個結點走,定一個標准,比如優先走右手邊的路,然后在到達下一個結點前先敲敲門,當一個結點的所有門都被敲了個遍都標記過,那么就走回頭路,再重復敲門,直到返回起點,這樣的方式我們叫做 DFS 深度優先遍歷,本文以圖結構講解,例子取自《大話數據結構》。
如我剛才所講,從A點出發,將路徑畫出來就是以下效果。
實線是走過的路程,虛線就是我們的小人敲門然后發現標記過的一個過程,大家可以寄幾模擬一哈。一句話總結就是:
從圖中某個頂點 v 出發,訪問此頂點,然后從 v 的未被訪問的鄰接點出發 深度優先遍歷圖結構,直至圖中所有和 v 有路徑相通的頂點都被訪問到。
結構定義代碼:
typedef char VertexType; typedef int EdgeType; #define MAXVEX 10 #define INFINITY 65535 typedef int boolean; boolean visited[MAXVEX]; typedef struct { VertexType vexs[MAXVEX]; EdgeType arc[MAXVEX][MAXVEX]; int numVertexes,numEdges; }MGraph;
鄰接矩陣創建:
void CreateMGraph(MGraph *G) { int i,j,k; printf("請輸入頂點數和邊數(空格隔開)\n"); scanf("%d %d",&G->numVertexes,&G->numEdges); printf("請依次輸入每個頂點的內容:\n"); for(i = 0;i < G->numVertexes;i++) { scanf("%c",&G->vexs[i]); } for(i = 0;i < G->numVertexes;i++) { for(j = 0;j < G->numVertexes;j++) { G->arc[i][j] = INFINITY; } } for(k = 0;k < G->numEdges;k++) { printf("輸入邊(vi,vj)上的下標i,下標j:\n"); scanf("%d %d",&i,&j); G->arc[i][j] = 1; G->arc[j][i] = G->arc[i][j]; } }
DFS算法
void DFS(MGraph G,int i) //深度優先遞歸算法 { int j; visited[i] = 1; printf("%c",G.vexs[i]); for(j = 0;j < G.numVertexes;j++) { if(G.arc[i][j] == 1 && !visited[j]) DFS(G,j); } } void DFStraverse(MGraph G) //深度遍歷 { int i; for(i = 0;i < G.numVertexes;i++) visited[i] = 0; for(i = 0;i < G.numVertexes;i++) { if(!visited[i]) DFS(G,i); } }
這種方法比較好理解在於使用循環進入函數再遞歸,可以保證以鄰接矩陣為儲存單位的每一個格子都被遍歷到,且做好標注,那么用鄰接矩陣的DFS算法時間復雜度可以想見是 O(n²),嵌套兩重循環,
我們來看下一種實現方式,這次我們使用的是鄰接單鏈表
結構定義:
typedef int boolean; boolean visited[MAXVEX]; typedef char VertexType; typedef int EdgeType; #define MAXVEX 10 #define INFINITY 65535 typedef struct EdgeNode //邊表結構點 { int adjvex; struct EdgeNode *next; }EdgeNode; typedef struct VertexNode //頂點表結構點 { VertexType data; EdgeNode *firstedge; }VertexNode,AdjList[MAXVEX]; typedef struct //總表結構 { AdjList adjList; int numVertexes,numEdges; }GraphAdjList;
比鄰接矩陣復雜一點,但是其結構只有三種,總表、定點表和邊表
創建:
void CreateALGraph(GraphAdjList *G) { int i,j,k; EdgeNode *e; printf("請輸入頂點數和邊數(空格隔開)\n"); scanf("%d %d",&G->numVertexes,&G->numEdges); for(i = 0;i < G->numVertexes;i++) { scanf("%c",&G->adjList[i].data); G->adjList[i].firstedge = NULL; } for(k = 0;k < G->numVertexes;k++) { printf("輸入邊(vi,vj)上的下標i,下標j:\n"); scanf("%d %d",&i,&j); e = (EdgeNode*)malloc(sizeof(EdgeNode)); e->adjvex=i; e->next = adjList[j].firstedge; adjList[j].firstedge = e; e = (EdgeNode*)malloc(sizeof(EdgeNode)); e->adjvex=j; e->next = adjList[i].firstedge; adjList[i].firstedge = e; } }
DFS算法實現:
void DFS(GraphAdjList GL,int i) { EdgeNode *p; visited[i] = 1; printf("%c",GL->adjList[i].data); while(p) { if(!visited[p->adjvex]) DFS(GL,p->adjvex); p = p->next; } } void DFStraverse(GraphAdjList GL) { int i; for(i = 0;i < GL->numVertexes;i++) visited[i] = 0; for(i = 0;i < GL->numVertexes;i++) { if(!visited[i]) DFS(GL,i); } }
利用鄰接表的方式能夠實現相同效果的遍歷,同時這種方法的算法時間復雜度為 O(n+e)
顯然對於點多邊少的稀疏圖來說,鄰接表結構使得算法在時間效率上大大提高。