1. 程式人生 > >圖的廣度遍歷和深度遍歷

圖的廣度遍歷和深度遍歷

初始化 -- fin num 方法 技術分享 else 全部 nts

/*
圖的遍歷方法主要有兩種:一種是深度優先遍歷。一種是廣度優先遍歷。

圖的深度優先遍歷類同於樹的先根遍歷。圖的廣度遍歷類同樹的層次遍歷
一:連通圖的深度優先遍歷算法
圖的深度優先遍歷算法是遍歷時深度優先的算法,即在圖的全部鄰接頂點中,每次都在訪問當前頂點後。首先訪問當前頂點的第一個鄰接頂點。
連通圖的深度優先遍歷遞歸算法例如以下:
1.訪問頂點v並標記頂點v已訪問。


2.查找頂點v的第一個鄰接頂點w。


3.若頂點v的鄰接頂點w存在。則繼續運行,否則算法結束。
4.若頂點w尚未被訪問,則深度優先遍歷遞歸訪問頂點w。


5.查找頂點v的w的鄰接頂點的下一個鄰接w。轉到步驟3


二:連通圖的廣度優先遍歷算法
圖的廣度優先遍歷算法是一個分層搜索的過程。廣度優先算法遍歷是指。從指定頂點開始,依照到該頂點路徑長度由短到長的順序,訪問圖
中德其余頂點。
連通圖的廣度優先遍歷算法例如以下:
1.訪問初始頂點v並標記頂點v為已訪問.
2.頂點v入隊列。
3.若隊列非空,則繼續運行。否則算法結束
4.出隊列取得隊頭頂點u。


5.查找頂點u的第一個鄰接頂點w。
6.若頂點u的鄰接頂點w不存在,則轉到步驟3,否則循環運行:
a.若頂點w尚未被訪問,則訪問頂點w並標記頂點w為已訪問;
b.頂點w入隊列;
c.查找頂點u的w鄰接頂點後的下一個鄰接頂點w,轉到步驟6






三:非連通圖的遍歷算法
對於非連通圖。從圖的隨意一個頂點開始深度或廣度優先遍歷一定能夠訪問圖中的全部頂點。但對於非連通圖,
從圖的隨意一個初始頂點開始深度或廣度優先遍歷。並不能訪問圖中的全部頂點,此時僅僅能訪問和初始頂點連通的那些頂點。
可是,對於非連通圖,能夠依次把每一個頂點都作為一次初始頂點進行深度優先遍歷或廣度優先遍歷。並依據每一個頂點的訪問標誌
來推斷該頂點是否已經訪問過。若尚未訪問過,則訪問之。否則跳過該頂點。

這樣就一定能夠訪問非連通圖中的多有頂點。

*/


實現代碼例如以下:

//鄰接矩陣存儲結構下圖操作的實現
#include<stdio.h>
#include<malloc.h>
#define MaxSize 100        //定義元素的大小
typedef char DataType;        //定義一個類型
#define MaxVertices 10       //定義頂點的最大值
#define MaxWeight 10000        //定義無窮大的詳細值


#define MaxQueueSize 15
typedef int DataType1;
typedef struct{

	DataType1 queue[MaxQueueSize];
	int rear;//隊列指針
	int front;//對頭指針
	int count;//計數器
}SeqCQueue;


//初始化
void QueueInitiate(SeqCQueue *q){//初始化順序循環隊列q

	q->count=0;//定義初始計數器
	q->rear=0;//定義初始隊尾指針下標
	q->front=0;//定義初始對頭指針標
}


//非空
int QueueNotEmpty(SeqCQueue q){

	//推斷順序循環隊列q非空否,非空則返回1,否則返回0
	if(q.count!=0){
	
		return 1;
	}else{
	
		return 0;
	}
}


//入隊列
int QueueAppend(SeqCQueue *q,DataType1 x){

	//把數據元素值x插入順序循環隊列q的隊尾,成功則返回1,失敗返回0
	if(q->count>0&&q->rear==q->front){//隊滿推斷
		printf("隊列已滿無法插入!!\n");
		return 0;
	}else{
	
		q->queue[q->rear]=x;//數據元素x插入隊尾
		q->rear=(q->rear+1)%MaxQueueSize;//隊尾指示器加1
		q->count++;//計數器加1
		return 1;//返回1
	}

}


//出隊列
int QueueDelete(SeqCQueue *q,DataType1 *d){
//刪除順序循環隊列q的隊頭元素並賦給d,成功返回1,失敗則返回0
	if(q->count==0){//對空推斷
	
		printf("隊列已空無數據元素出隊列!!\n");
		return 0;
	}else{
	
		*d=q->queue[q->front];//取對頭元素存入d中
		q->front=(q->front+1)%MaxQueueSize;//對頭指示器加1
		q->count--;//計算器減一
		return 1;
	}
}


//取對頭數據元素
int QueueGet(SeqCQueue q,DataType1 *d){

	//取順序循環隊列q的當前對頭元素並賦給d,成功則返回1。失敗則返回0
	if(q.count==0){
	
		printf("隊列已空,無數據元素能夠取!!\n");
		return 0;
	}else{
	
		*d=q.queue[q.front];
		return 1;
	}
}


typedef struct{     //定義一個結構體
  DataType list[MaxSize];
  int size;             //結構體元素的大小
}SeqList;                 //結構體的對象

typedef struct{

	SeqList Vertices;//存放頂點的順序表
	int edge[MaxVertices][MaxVertices];//存放邊的鄰接矩陣
	int numOfEdges;//邊的條數
}AdjMGraph;


typedef struct{

	int row;//行下標
	int col;//列下標
	int weight;//權值
}RowColWeight;//邊信息結構體

//初始化
void  initiate(SeqList *L){
      L->size=0;//定義初始化元素個數
}

//求當前元素的個數
int getLength(SeqList L){

	return L.size;//返回長度
}

//插入數據元素
int insertData(SeqList *L,int i,DataType x){
	//在順序表L的第i(0<=i<=size)個位置前插入數據元素x
	//插入成功返回1,出人失敗返回0
   int j;
   if(L->size>=MaxSize){
      printf("順序表已滿,無法插入!!\n");
	  return 0;
   }else if(i<0||i>L->size){
      printf("插入的位置不合法,不在指定的範圍。參數i不合法!\n");
	  return 0;
   }else{
      //從後向前一致移動數據。為插入做準備
	   for(j=L->size;j>i;j--){
	         L->list[j]=L->list[j-1];
	   }
       L->list[i]=x;
	   L->size++;
	   return 1;
   }
}

//刪除數據
int deleteData(SeqList *L,int i,DataType *x){ 
    //刪除順序表中位置為i的數據i>=0&&i<=size-1。把數據保存到x中
	//刪除成功返回1,否則返回0
	int j;
	if(L->size<=0){
	    printf("順序表已空無數據元素可刪!\n");
		return 0;
	}else if(i<0||i>L->size-1){
	    printf("參數i不合法。不能刪除!\n");
		return 0;
	}else{
		*x=L->list[i];
		for(j=i+1;j<=L->size-1;j++){//從前往後一次前移
		     L->list[j-1]=L->list[j];
		}
		L->size--;//數據元素減一
		return 1;
	}
}

//取出數據元素
int getData(SeqList L,int i,DataType *x){
    if(i<0||i>L.size-1){
		printf("參數i不合法。不能刪除!\n");
		return 0;
	}else{
	    *x=L.list[i];
		return 1;
	}
}



//初始化有n個頂點的順序表和鄰接矩陣
void InitiateG(AdjMGraph *g,int n){

	//初始化
	int i,j;
	
	for(i=0;i<n;i++){
	
		for(j=0;j<n;j++){
			
			if(i==j){
			
				g->edge[i][j]=0;
			}else{
			
				g->edge[i][j]=MaxWeight;//MaxWeight表示無窮大
			}
		}
	}

	g->numOfEdges=0;//邊的條數置為0
	initiate(&g->Vertices);//順序表初始化

}


//插入頂點
void InsertVertex(AdjMGraph *g,DataType vertex){
//在圖G中插入頂點vertex

	insertData(&g->Vertices,g->Vertices.size,vertex);//順序表尾插入

}


//插入邊
void InsertEdge(AdjMGraph *g,int v1,int v2,int weight){
//在圖中插入邊<v1,v2>,邊<v1,v2>的權為weight
	if(v1<0||v1>=g->Vertices.size||v2<0||v2>=g->Vertices.size){
	
		printf("參數v1或v2越界出錯!!!\n");
		return ;
	}

	g->edge[v1][v2]=weight;
	g->numOfEdges++;

}


//刪除邊
void DeleteEdge(AdjMGraph *g,int v1,int v2){

	//在G圖中刪除邊<v1,v2>
	if(v1<0||v1>=g->Vertices.size||v2<0||v2>=g->Vertices.size){
	
		printf("參數v1或v2越界出錯!!!\n");
		return ;
	}

	if(g->edge[v1][v2]==MaxWeight||v1==v2){
	
		printf("該邊不存在!!!\n");
		return;
	}


	g->edge[v1][v2]=MaxWeight;
	g->numOfEdges--;


}


//取第一個鄰接頂點
int GetFirstVex(AdjMGraph g,int v){
//在圖G中尋找序號為v的頂點的第一個鄰接頂點
	//假設這種頂點存在,則返回該鄰接頂點的序號,否則返回-1
	int col;
	if(v<0||v>=g.Vertices.size){
	
		printf("參數v1越界出錯!!!\n");
		return -1;
	}

	for(col=0;col<g.Vertices.size;col++){
	
		if(g.edge[v][col]>0&&g.edge[v][col]<MaxWeight){
		
			return col;
		}
	}

	return -1;

}


//取下一個鄰接頂點
int GetNextVex(AdjMGraph g,int v1,int v2){
//在圖中尋找v1頂點的鄰接頂點v2的下一個鄰接頂點
	//假設這種鄰接頂點存在,則返回該鄰接頂點的序號。否則返回-1
	//v1和v2都是對應的頂點的序號
	int col;
	if(v1<0||v1>g.Vertices.size||v2<0||v2>=g.Vertices.size){
		printf("參數v1或v2越界出錯!!!\n");
		return -1;
	}


	for(col=v2+1;col<g.Vertices.size;col++){
	
		if(g.edge[v1][col]>0&&g.edge[v1][col]<MaxWeight){
		
			return col;
		}
	}

	return -1;

}


void CreatGraph(AdjMGraph *g,DataType V[],int n,RowColWeight E[],int e){
//在圖中插入n個頂點信息V和e條邊信息E
	int i,k;
	InitiateG(g,n);//d頂點順序表初始化
	for(i=0;i<n;i++){
	
		InsertVertex(g,V[i]);//插入頂點
	}
	for(k=0;k<e;k++){
		InsertEdge(g,E[k].row,E[k].col,E[k].weight);//插入邊
	}
}

void Visit(DataType item){//定義訪問操作函數 

	printf("%c   ",item);
}

//連通圖的深度優先函數
void DepthFSearch(AdjMGraph g,int v,int visited[],
				  void Visit(DataType item)){
	//連通圖G以v為初始頂點的訪問操作作為Visit()的深度優先遍歷
	//數組visited標記了對應頂點是否已經訪問過,0表示未訪問。1表示已經訪問
	int w;
    Visit(g.Vertices.list[v]);//訪問頂點v
	visited[v]=1;//置已訪問標誌
	w=GetFirstVex(g,v);//取第一個鄰接頂點
	while(w!=-1){
	
		if(!visited[w]){
			DepthFSearch(g,w,visited,Visit);//遞歸
			
		}
		w=GetNextVex(g,v,w);
	}
}

//非連通圖的深度優先遍歷函數
void DepthFirstSearch(AdjMGraph g,void Visit(DataType item)){
//非連通圖G的訪問操作作為Visit()的深度優先遍歷
	int i;
	int *visited=(int *)malloc(sizeof(int)*g.Vertices.size);
	for(i=0;i<g.Vertices.size;i++){
	
		visited[i]=0;//訪問標誌初始均為0
	}
	for(i=0;i<g.Vertices.size;i++){
		
		if(!visited[i]){
		
			DepthFSearch(g,i,visited,Visit);//以每個頂點為初始頂點進行調用
			
		}
		
	}
    
	free(visited);
}


//連通圖的廣度優先遍歷函數
void BrodFSearch(AdjMGraph g,int v,int visited[],
				 void Visit(DataType item)){
//連通圖G以v為初始頂點訪問操作為Visit廣度優先遍歷
	//數組visited標記了對應頂點是否已訪問過。0表示未訪問,1表示已訪問
	DataType1 u,w;
	SeqCQueue queue;
    Visit(g.Vertices.list[v]);//訪問頂點v
	visited[v]=1;//置已訪問標誌
    QueueInitiate(&queue);//隊列初始化
    QueueAppend(&queue,v);//初始頂點v入隊列
    while(QueueNotEmpty(queue)){
		QueueDelete(&queue,&u);//出隊列
		w=GetFirstVex(g,u);//取頂點u的第一個鄰接頂點
		while(w!=-1){//鄰接頂點w存在時
			if(!visited[w]){//若沒有訪問過
			
				Visit(g.Vertices.list[w]);//訪問頂點w
				visited[w]=1;//置已訪問標誌
				QueueAppend(&queue,w);//頂點w入隊列

			}
			w=GetNextVex(g,u,w);//取下一個鄰接頂點
		}
	}

}


//非連通圖的廣度優先遍歷函數例如以下:
void BroadFirstSearch(AdjMGraph g,void Visit(DataType item)){

	//非連通圖G訪問操作為Visit()的廣度優先遍歷
	int i;
	int *visited=(int *)malloc(sizeof(int)*g.Vertices.size); 
	for(i=0;i<g.Vertices.size;i++){
	
		visited[i]=0;//訪問標誌初始均為0
	}

	for(i=0;i<g.Vertices.size;i++){
	
		if(!visited[i]){
		
			BrodFSearch(g,i,visited,Visit);//以每個頂點為初始頂點進行調用
		}
	}


	free(visited);


}



void main(){

	AdjMGraph g;
	DataType a[]={‘A‘,‘B‘,‘C‘,‘D‘,‘E‘};
	RowColWeight rcw[]={{0,1,10},{0,4,20},{1,3,30},{2,1,40},{3,2,50}};
	int n=5,e=5;
	int i,j;
	CreatGraph(&g,a,n,rcw,e);//創建圖
    

	printf("深度優先遍歷序列為:");
	DepthFirstSearch(g,Visit);
    
	printf("\n廣度優先遍歷序列為:");
	
    BroadFirstSearch(g,Visit);

	printf("\n");

}




技術分享
































圖的廣度遍歷和深度遍歷