C语言-深度优先遍历

  1. 查找函数(LocateVex查找坐标)
  2. 构建无向图(Undirected Graph)
  3. 输出邻接矩阵(print)
  4. 深度优先查找函数(DFS)
  5. 深度优先遍历(DFSTraverse)

图的遍历

  1. 深度优先遍历(DFS):类似于树中的先序遍历,整体思想是:先输出当前结点,在根据一定的次序去递归查找孩子。
  2. 广度优先遍历(BFS):类似于树中的层次遍历,需要用队列来体现结点访问的次序关系。
    C语言-深度优先遍历_第1张图片

图的遍历需要考虑的问题

  1. 图是一种多对多的关系,每一个顶点有若干个,结点之间也有若干条回路,每一个连通图都有若干种路径,那么我们怎么知道结点是否已经被访问过?
  • 设置 "标志"数组 visited,数组每一位都对应一个结点的访问状态,0代表未被访问,1代表已访问。并且visited数组要设置成全局变量
  1. 连通图与非连通图的问题
  • 如果是连通图,根据程序递归,以任意一个端点为起点都可以走完整个图
    C语言-深度优先遍历_第2张图片
  • 如果是非连通图,存在“落单”的端点,“孤岛”端点,既无法一次走完整个图,此时就需要多次调用遍历函数来保证每一个结点都被访问到。
    C语言-深度优先遍历_第3张图片
  • 回到算法设计上而言,也就是解释了为什么会出现DFS、DFSTraverse两个函数,其实,如果是连通图由DFS函数就可以完成对整个图的遍历,DFSTraverse里面的for(i=0;ivexnum;i++)(邻接矩阵第134-139行,邻接表第150-155行)就是为了非连通图而准备的。
  • 由水流的例子,我们还可以知道:
    C语言-深度优先遍历_第4张图片

深度优先遍历基本步骤

  1. 设置全局变量visited数组并初始化为全0,代表所有节点均未被访问
  2. 根据端点存放顺序:
  • 2.1 选取初始端点:对初始端点进行访问,并在visited数值中标记成已访问状态(代码演示的初始端点是G->Vertex[i],此时i=0)
  • 2.2 由初始端点开始对后续端点进行处理
    步骤2为深度优先搜索
  1. 循环对所有节点都执行步骤2,前提是该节点未被访问!(对应函数DFSTraverse,主要用于非连通图能访问到每一个结点)
    步骤3 循环执行深度优先搜索,就是深度优先遍历

C语言-深度优先遍历_第5张图片

完整源代码:

  1. 注:图的遍历算法在任何图与网中都适用,此处仅用无向图UDG进行演示!
  2. 注:邻接矩阵和邻接表对同一个图的深度优先遍历顺序是不一样的!因为此处邻接表的边关系是由头插法的形式插入,若采用尾插法则遍历序列与邻接矩阵一致

1.邻接矩阵的深度优先遍历

#include 
#include 
#define VertexMax 100 //最大顶点数为100

typedef char VertexType; //每个顶点数据类型为字符型 

typedef struct
{
	VertexType Vertex[VertexMax];//存放顶点元素的一维数组 
	int AdjMatrix[VertexMax][VertexMax];//邻接矩阵二维数组 
	int vexnum,arcnum;//图的顶点数和边数  
}MGraph;

int LocateVex(MGraph *G,VertexType v)//查找元素v在一维数组 Vertex[] 中的下标,并返回下标 
{
	int i;
	
	for(i=0;i<G->vexnum;i++)
	{
		if(v==G->Vertex[i])
		{
			return i; 
		} 
	 } 
	 
	 printf("No Such Vertex!\n");
	 return -1;
}

//无向图
void CreateUDG(MGraph *G) 
{
	int i,j;

	printf("输入顶点个数和边数:\n");
	printf("顶点数 n="); 
	scanf("%d",&G->vexnum);
	printf("边  数 e="); 
	scanf("%d",&G->arcnum);
	printf("\n"); 
	
	printf("\n");
	

	printf("输入顶点元素(无需空格隔开):");
	scanf("%s",G->Vertex);
	printf("\n");

	for(i=0;i<G->vexnum;i++) 
	 for(j=0;j<G->vexnum;j++)
	    {
	    	G->AdjMatrix[i][j]=0;
		}
	

	 int n,m;
	 VertexType v1,v2;
	 
	 printf("请输入边的信息:\n");
	 for(i=0;i<G->arcnum;i++)
	 {
	 	scanf(" %c%c",&v1,&v2);
	 	n=LocateVex(G,v1); 
	 	m=LocateVex(G,v2); 
	 	
	 	if(n==-1||m==-1)
		 {
		 	printf("NO This Vertex!\n");
		 	return;
		  } 
	
	   G->AdjMatrix[n][m]=1;
	   G->AdjMatrix[m][n]=1;
	 } 
	 
}

void print(MGraph G)
{
	int i,j;
	printf("\n-------------------------------");
	printf("\n 邻接矩阵:\n\n"); 	

		printf("\t ");
	    for(i=0;i<G.vexnum;i++)
		printf("  %c",G.Vertex[i]);
		printf("\n");
		 
		for(i=0;i<G.vexnum;i++)
	   {
	   	  printf("\t%c",G.Vertex[i]);
	   	
		  for(j=0;j<G.vexnum;j++)
	    {
	 	    printf("  %d",G.AdjMatrix[i][j]);
	    }
	        printf("\n");
	   }
}

/*深度优先遍历DFS*/ 
int visited[VertexMax];//定义"标志"数组为全局变量 

void DFS(MGraph *G,int i)
{
	int j;
	
	//1.处理起始点 
	printf("%c",G->Vertex[i]);//1.输出起始结点 
	visited[i]=1;//2.将已访问的结点标志成1
	
	 //2.由起始点开始,对后续结点进行操作
	for(j=0;j<G->vexnum;j++)//依次搜索vi的邻接点 
	{
		if(G->AdjMatrix[i][j]==1&&visited[j]==0)//当满足有边且未被访问过时,递归调用去查找该邻接点 
		{
			DFS(G,j);//注意此处的G已经是指针类型,不需要再&G 
		}
	}
	
}

void DFSTraverse(MGraph *G) 
{
	int i;
	
	//初始化"标志"数组为0,代表未访问
	for(i=0;i<G->vexnum;i++)
	{
		visited[i]=0; 
	} 
	
	for(i=0;i<G->vexnum;i++)
	{
		if(visited[i]==0)
		{
			DFS(G,i);//注意此处的G已经是指着类型,不需要再&G 
		}
	} 
	
}

int main() 
{
	MGraph G;
	CreateUDG(&G);
	print(G); 

	printf("\n\n深度优先遍历:"); 
	DFSTraverse(&G); 
	 
	return 0;
}	
	

2.邻接表的深度优先遍历:

#include 
#include 
#define VertexType char //顶点的数据类型(char) 
#define VertexMax 20 //最大顶点个数 

typedef struct ArcNode//边表 
{
	int adjvex;//存储的是该顶点在顶点数组即AdjList[]中的位置 
	struct ArcNode *next;
}ArcNode;

typedef struct VNode //顶单个点 
{
	VertexType vertex;
	struct ArcNode *firstarc;
}VNode;

typedef struct //顶点表 
{
	VNode AdjList[VertexMax];//由顶点构成的结构体数组 
	int vexnum,arcnum; //顶点数和边数 
}ALGraph;

int LocateVex(ALGraph *G,VertexType v)
{    
    int i;
	for(i=0;i<G->vexnum;i++)
	{
		if(v==G->AdjList[i].vertex)
		{
			return i;
		}
	}
	
	printf("No Such Vertex!\n");
	return -1;
}

//2.无向图 
void CreateUDG(ALGraph *G)
{
	int i,j;
	//1.输入顶点数和边数
	printf("输入顶点个数和边数:\n");
	printf("顶点数 n="); 
	scanf("%d",&G->vexnum);
	printf("边  数 e="); 
	scanf("%d",&G->arcnum);
	printf("\n"); 
	
	printf("\n");
	//2.顶点表数据域填值初始化顶点表指针域
	printf("输入顶点元素(无需空格隔开):");
	for(i=0;i<G->vexnum;i++)
	{
		scanf(" %c",&G->AdjList[i].vertex);
		G->AdjList[i].firstarc=NULL;
	} 
	printf("\n");
	
	//3.输入边信息构造邻接表
	int n,m;
	VertexType v1,v2;
	ArcNode *p1,*p2; 
	
	printf("请输入边的信息:\n\n"); 
	for(i=0;i<G->arcnum;i++)
	{   //输入边信息,并确定v1和v2在G中的位置,即顶点在AdjList[]数组中的位置(下标) 
		printf("输入第%d条边信息:",i+1); 
		scanf(" %c%c",&v1,&v2);
		n=LocateVex(G,v1);
		m=LocateVex(G,v2);
		
		if(n==-1||m==-1)
		 {
		 	printf("NO This Vertex!\n");
		 	return;
		  } 
		
		p1=(ArcNode *)malloc(sizeof(ArcNode));
		p1->adjvex=m;//填上坐标 
		p1->next=G->AdjList[n].firstarc;//改链(头插法) 
		G->AdjList[n].firstarc=p1;
		
		p2=(ArcNode *)malloc(sizeof(ArcNode));//无向图的对称 
		p2->adjvex=n;
		p2->next=G->AdjList[m].firstarc;
		G->AdjList[m].firstarc=p2;
		
	}//for 

} 

void print(ALGraph G)
{
	int i;
	ArcNode *p;
	printf("\n-------------------------------");
	printf("\n图的邻接表表示:\n");
	
	for(i=0;i<G.vexnum;i++)
	{
		printf("\n   AdjList[%d]%4c",i,G.AdjList[i].vertex);
		p=G.AdjList[i].firstarc;
		
		while(p!=NULL)
		{
			printf("-->%d",p->adjvex);
			p=p->next;
		}
	 } 
	 printf("\n");
} 

/*深度优先遍历*/
int visited[VertexMax]; //定义数组为全局变量 

void DFS(ALGraph *G,int i)
{
	int j;
	struct ArcNode *p;
	int w;
	
	printf("%c",G->AdjList[i].vertex);//1.访问当前节点 
	visited[i]=1;//2.将当前节点标记成已访问结点
	
	p=G->AdjList[i].firstarc;
	while(p!=NULL)
	{
		w=p->adjvex;//w是邻接点的坐标 
		if(visited[w]==0)
		{
			DFS(G,w);
		}
	       p=p->next;//查找完之后,将p向后推一位 
	}
	 
} 

void DFSTraverse(ALGraph *G)
{
	int i;
	
	//初始化"标志"数组
	for(i=0;i<G->vexnum;i++)
	{
		visited[i]=0;
	} 
	
	for(i=0;i<G->vexnum;i++)
	{
		if(visited[i]==0)
		{
			DFS(G,i);
		}
	}
}

int main()
{
 	ALGraph G;
 	CreateUDG(&G);
	print(G);
	 
	printf("\n\n深度优先遍历:"); 
	DFSTraverse(&G);  
 	
	return 0;
}

执行结果:

  • 邻接矩阵
    C语言-深度优先遍历_第6张图片
  • 邻接表
    C语言-深度优先遍历_第7张图片

你可能感兴趣的:(数据结构,dfs,c语言,链表,数据结构,图论)