1. 程式人生 > >圖的深度優先遍歷DFS(鄰接表實現)c語言

圖的深度優先遍歷DFS(鄰接表實現)c語言

要實現該演算法首先要知道鄰接表的概念。

鄰接表是一種常用的圖的儲存結構,它的結構特點是:

頂點由一個一維陣列儲存;

鄰接點用連結串列儲存

相比於單純用陣列實現的鄰接矩陣,鄰接表可以避免空間浪費

其圖解如下:


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函式就可以寫成上述的形式了。