圖的深度優先遍歷DFS(鄰接表實現)c語言
阿新 • • 發佈:2019-02-08
要實現該演算法首先要知道鄰接表的概念。
鄰接表是一種常用的圖的儲存結構,它的結構特點是:
頂點由一個一維陣列儲存;
鄰接點用連結串列儲存
相比於單純用陣列實現的鄰接矩陣,鄰接表可以避免空間浪費
其圖解如下:
firstedge指向邊表第一個結點。
邊表的adjvex的值代表與V0頂點有邊的頂點下標,例:
A的firstedge指向邊表的1而後指向2,3,意思是A與B,C,D各有一條共同的邊。
演算法的時間複雜度:對於n個頂點e條邊的圖,找鄰接點所需的時間取決於頂點和邊的數量,故為O(n+e)
鄰接表結構程式碼實現如下:
/*鄰接表結構*/ typedef char VertexType; //頂點型別 typedef int EdgeType; //權值型別 /*邊表結點*/ typedef struct EdgeNode { int adjvex; //鄰接點域,儲存鄰接點下標 EdgeType weight; //儲存權值,非網圖則不需要 struct EdgeNode *next; //鏈域,指向下一個鄰接點 }EdgeNode; typedef struct VertexNode { VertexType data; //頂點域 EdegeNode *firstedge; //邊表頭指標 }VertexNode,AdList[MAX]; typedef struct { AdjList adjList; int numVertexes,numEdges; //頂點數量和邊數量 }GraphAdjList,*GraphAdj;
建立鄰接表:
/*鄰接表建立*/ void create(GraphAdj G) { int i,j,k; EdgeNode *e; printf("輸入頂點數,邊數:"); 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->numEdges;k++) //建立邊表 { printf("輸入邊(Vi,Vj)上的頂點序號:"); scanf("%d%d",&i,&j); /*使用頭插法加入邊表結點*/ e=(EdgeNode *)malloc(sizeof(EdgeNode)); //生成邊表結點 e->adjvex=j; e->next=G->adjList[i].firstedge; G->adjList[i].firstedge=e; e=(EdgeNode *)malloc(sizeof(EdgeNode)); //生成邊表結點 e->adjvex=i; e->next=G->adjList[j].firstedge; G->adjList[j].firstedge=e; } }
鄰接表的深度優先遍歷
鄰接表的深度優先遍歷同鄰接矩陣的深度優先遍歷大同小異,都需要建立一個標誌陣列,陣列存放bool型別成員TRUE,FALSE。 其中TRUE表示已經訪問過。
不同點:在遞迴函式中需要宣告一個EdgeNode *型別的變數p來進行邊表的遍歷,也就是在邊表的連結串列中遍歷鄰接點。
實現程式碼如下:
/*鄰接表的深度優先遞迴*/ void DFS(GraphAdj G,int i) { EdgeNode *p; visited[i]=TRUE; //訪問過了該頂點,標記為TRUE printf("%c",G->adjList[i].data); p=G->adjList[i].firstedge; //讓p指向邊表第一個結點 while(p) //在邊表內遍歷 { if(!visited[p->adjvex]) //對未訪問的鄰接頂點遞迴呼叫 DFS(G,p->adjvex); p=p->next; } } //鄰接表的深度遍歷操作 void DFSTraverse(GraphAdj G) { int i; for(i=0;i<G->numVertexes;i++) visited[i]=FALSE; //初始設定為未訪問 for(i=0;i<G->numVertexes;i++) if(!visited[i]) DFS(G,i); //對未訪問的頂點呼叫DFS,若是連通圖只會執行一次 }
完整程式碼:
#include<stdio.h>
#include<stdlib.h>
#define MAX 10
#define INIFINITY 65535
#define TRUE 1
#define FALSE 0
typedef int Boole; //布林型別 儲存TRUE FALSE
Boole visited[MAX]; //訪問標誌陣列
//鄰接表結點定義
typedef char VertexType; //頂點資料型別
typedef int EdgeType; //邊上的權值型別
typedef struct EdgeNode //邊表結點 儲存邊表資訊
{
int adjvex; //鄰接點域,儲存該頂點對應的下標
EdgeType weight; //權值
struct EdgeNode *next; //鏈域,指向下一個鄰接點
}EdgeNode;
typedef struct VertexNode //頂點表結點
{
VertexType data; //頂點域,儲存頂點資訊
EdgeNode *firstedge; //邊表頭指標,指向此頂點的第一個鄰接點
}VertexNode,AdjList[MAX];
typedef struct
{
AdjList adjList;
int numVertexes,numEdges; //圖中當前頂點數和邊數
}GraphAdjList,*GraphAdj;
/*鄰接表建立*/
void create(GraphAdj G)
{
int i,j,k;
EdgeNode *e;
printf("輸入頂點數和邊數:");
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->numEdges;k++) //建立邊表
{
printf("輸入邊(Vi,Vj)上的頂點序號:");
scanf("%d%d",&i,&j);
/*使用頭插法加入邊表結點*/
e=(EdgeNode *)malloc(sizeof(EdgeNode)); //生成結點
e->adjvex=j;
e->next=G->adjList[i].firstedge;
G->adjList[i].firstedge=e;
e=(EdgeNode *)malloc(sizeof(EdgeNode)); //生成結點
e->adjvex=i;
e->next=G->adjList[j].firstedge;
G->adjList[j].firstedge=e;
}
}
/*鄰接表的深度優先遞迴*/
void DFS(GraphAdj G,int i)
{
EdgeNode *p;
visited[i]=TRUE; //訪問過了該頂點,標記為TRUE
printf("%c",G->adjList[i].data);
p=G->adjList[i].firstedge; //讓p指向邊表第一個結點
while(p) //在邊表內遍歷
{
if(!visited[p->adjvex]) //對未訪問的鄰接頂點遞迴呼叫
DFS(G,p->adjvex);
p=p->next;
}
}
//鄰接表的深度遍歷操作
void DFSTraverse(GraphAdj G)
{
int i;
for(i=0;i<G->numVertexes;i++)
visited[i]=FALSE; //初始設定為未訪問
for(i=0;i<G->numVertexes;i++)
if(!visited[i]) //對未訪問的頂點呼叫DFS,若是連通圖只會執行一次
DFS(G,i);
}
int main()
{
GraphAdjList G;
create(&G);
printf("\n");
DFSTraverse(&G);
printf("\n圖遍歷完畢");
return 0;
}
錯誤警示:
一開始我在main函式中寫的是
int main()
{
GraphAdj G;
create(G);
printf("\n");
DFSTraverse(G);
printf("\n圖遍歷完畢");
return 0;
}
可是執行時會出現問題,後來我才知道,GraphAdj G需要初始化,指標變數在分配了記憶體空間,即先要有指向之後才可以引用其值
但若是在cpp中可以把create函式的形參改成:
GraphAdj &G
&的意思是傳進來節點指標的引用,括號內等價於 GraphAdj* &G,目的是讓傳遞進來的指標發生改變
使用引用後,main函式就可以寫成上述的形式了。