【C语言】DFS、BFS、Dijkstra算法、Prim算法代码实现

C语言——无向带权图的邻接表存储、DFS深度优先搜索、BFS广度优先搜索、Dijkstra算法、Prim算法代码实现

图的结构体设计

typedef struct GNode Gnode; 
typedef struct GNode{
	int nv,ne;//nv为顶点数,ne为边数 
	int G[MAXSIZE][MAXSIZE];//若无边则值为0,右边则存储边的权值 
	int visited[MAXSIZE];//顶点未被访问则值为0,否则为1 
	int dist[MAXSIZE];//Dijkstra算法中到开始顶点的距离 
	int path[MAXSIZE];//Dijkstra算法中,最短路径途径的上一个顶点索引 
	int dis[MAXSIZE];//Prim算法中记录的最小权值 
	int primParent[MAXSIZE];//Prim算法中某顶点的父亲节点索引 
}*MGraph;

结构设计中加入了实现Dijkstra和prim算法所需的数组

DFS代码

void DFS(MGraph Graph,int s)//从s顶点开始的深度优先搜索 
{
	printf("%d ",s);
	Graph->visited[s] = 1;//输出顶点s并记录访问 
	for(int i = 0;i < Graph->nv;i++)
	{
		//若顶点i与s有边存在且未被访问,则访问i并对i递归调用 
		if(!Graph->visited[i] && Graph->G[i][s])
		{
			Graph->visited[i] = 1;;
			DFS(Graph,i);
		}
	}
}
 
void controlDFS(MGraph Graph)//对每个顶点DFS,防止图不连通,有顶点没用被遍历 
{
	for(int i = 0;i < Graph->nv;i++)
	{
		if(!Graph->visited[i])
		{
			DFS(Graph,i);
		}
	}
	printf("\n"); 
}

BFS代码

BFS实现代码中运用的队列代码,也可用一个一维数组代替

//创建一个队列,用于BFS中存储顶点索引 
typedef struct Qnode Qnode;
typedef struct Qnode{
	int vertex;
	Qnode *next;
}*Queue;

bool isEmpty(Queue Q)//队列空则返回true 
{
	if(Q->next == NULL) return true;
	else return false;
}

void inQueue(int data,Queue Q)//入队 
{
	Queue p = (Queue)malloc(sizeof(Qnode));
	p->vertex = data;
	p->next = NULL;
	Queue tail = Q;
	while(tail->next!=NULL)
	{
		tail = tail->next;
	}
	tail->next = p;
}

int deQueue(Queue Q)//出队 
{
	if(!isEmpty(Q))
	{
		Queue firstCell = Q->next;
		Q->next = firstCell->next;
		int r = firstCell->vertex;
		free(firstCell);
		return r;
	}
	return -1; 
}

BFS主体代码

void BFS(MGraph Graph,int s)//从s顶点开始的广度优先搜索 
{	
	//创建一个空队列 
	Queue Q = (Queue)malloc(sizeof(Qnode)); 
	Q->next = NULL;
	inQueue(s,Q);//顶点s入队,并访问 
	Graph->visited[s] = 1; 
	while(!isEmpty(Q)) 
	{	
		//若顶点与i与s存在边且未被访问,则入队且标记已访问
		for(int i = 0;i < Graph->nv;i++)
		{
			if(Graph->G[s][i] && !Graph->visited[i])
			{
				inQueue(i,Q);
				Graph->visited[i] = 1;
			}
		}
		s = deQueue(Q);//出队一个顶点且设为s,循环
		printf("%d ",s);
	}
}

可增加一个已访问顶点计数器,或者遍历Graph->visited数组,检查是否存在顶点未被访问,即图不连通。

Dijkstra算法代码

void Dijkstra(MGraph Graph,int s)//各顶点到顶点s的最短路径 
{
	int min = 999;//用于寻找最短路径 
	int minV = -1;//用于存储最短路径的顶点索引 
	for(int i = 0;i < Graph->nv;i++)
	{	//初始化并后续更新Graph->dist数组中的最短路径 
		if(Graph->G[s][i] && !Graph->visited[i])
		{	 
			if(Graph->dist[s]+Graph->G[s][i] < Graph->dist[i])
			{
				Graph->path[i] = s;
				Graph->dist[i] = Graph->G[s][i] + Graph->dist[s];
			}
		}
		//寻找待收录顶点minV 
		if(Graph->dist[i] < min && !Graph->visited[i])
			{
				min = Graph->dist[i];
				minV = i;
			}
	}
	if(min == 999) return ;//若minV不存在,终止调用 
	Graph->visited[minV] = 1;

	Dijkstra(Graph,minV);//对新收录的顶点递归调用 
}

Prim算法代码

void prim(MGraph Graph,int s)//s为root的最小生成树 
{
	Graph->visited[s] = 1;//收录顶点s 
	
	int min = 999;//功能类似Dijkstra中的min与minV 
	int minV = -1;
	for(int i = 0;i < Graph->nv;i++)
	{
		//初始化与更新Graph->dis数组 
		if(Graph->G[s][i]&&!Graph->visited[i])
		{
			if(Graph->G[s][i] < Graph->dis[i])
			{
				Graph->dis[i] = Graph->G[s][i];
				Graph->primParent[i] = s;
			}
		}
		//寻找下一个待收录顶点 
		if(!Graph->visited[i])
		{
			if(Graph->dis[i] < min)
			{
				min = Graph->dis[i];
				minV = i;
			}
		}
	}
	if(min == 999) return ;//若minV不存在,终止调用 
	Graph->visited[minV] = 1;//收录minV且递归调用 
	prim(Graph,minV);
}

所有代码合并

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 20 //邻接表的顶点容量 

typedef struct GNode Gnode; 
typedef struct GNode{
	int nv,ne;//nv为顶点数,ne为边数 
	int G[MAXSIZE][MAXSIZE];//若无边则值为0,右边则存储边的权值 
	int visited[MAXSIZE];//顶点未被访问则值为0,否则为1 
	int dist[MAXSIZE];//Dijkstra算法中到开始顶点的距离 
	int path[MAXSIZE];//Dijkstra算法中,最短路径途径的上一个顶点索引 
	int dis[MAXSIZE];//Prim算法中记录的最小权值 
	int primParent[MAXSIZE];//Prim算法中某顶点的父亲节点索引 
}*MGraph;

void initArray(int *a,int n,int initValue)//初始化长度为n的数组a,值设为initValue 
{
	for(int i = 0;i < n;i++)
	{
		a[i] = initValue;
	}
}

void BuildGraph(MGraph &Graph)//建一个无向图 
{
	Graph = (MGraph)malloc(sizeof(Gnode));
	scanf("%d%d",&Graph->nv,&Graph->ne);//输入顶点数和边数 
	for(int i = 0;i < Graph->nv;i++) //初始化邻接表 
	{
		for(int j = 0;j < Graph->nv;j++)
		{
			Graph->G[i][j] = 0;
		}
	}
	int v1,v2,weight;//v1,v2代表边 weight代表权重 
	for(int i = 0;i < Graph->ne;i++)
	{
		scanf("%d%d%d",&v1,&v2,&weight);
		Graph->G[v1][v2] = Graph->G[v2][v1] = weight;
	}
}
 
void DFS(MGraph Graph,int s)//从s顶点开始的深度优先搜索 
{
	printf("%d ",s);
	Graph->visited[s] = 1;//输出顶点s并记录访问 
	for(int i = 0;i < Graph->nv;i++)
	{
		//若顶点i与s有边存在且未被访问,则访问i并对i递归调用 
		if(!Graph->visited[i] && Graph->G[i][s])
		{
			Graph->visited[i] = 1;;
			DFS(Graph,i);
		}
	}
}
 
void controlDFS(MGraph Graph)//对每个顶点DFS,防止图不连通,有顶点没用被遍历 
{
	for(int i = 0;i < Graph->nv;i++)
	{
		if(!Graph->visited[i])
		{
			DFS(Graph,i);
		}
	}
	printf("\n"); 
}

//创建一个队列,用于BFS中存储顶点索引 
typedef struct Qnode Qnode;
typedef struct Qnode{
	int vertex;
	Qnode *next;
}*Queue;

bool isEmpty(Queue Q)//队列空则返回true 
{
	if(Q->next == NULL) return true;
	else return false;
}

void inQueue(int data,Queue Q)//入队 
{
	Queue p = (Queue)malloc(sizeof(Qnode));
	p->vertex = data;
	p->next = NULL;
	Queue tail = Q;
	while(tail->next!=NULL)
	{
		tail = tail->next;
	}
	tail->next = p;
}

int deQueue(Queue Q)//出队 
{
	if(!isEmpty(Q))
	{
		Queue firstCell = Q->next;
		Q->next = firstCell->next;
		int r = firstCell->vertex;
		free(firstCell);
		return r;
	}
	return -1; 
}

void BFS(MGraph Graph,int s)//从s顶点开始的广度优先搜索 
{	
	//创建一个空队列 
	Queue Q = (Queue)malloc(sizeof(Qnode)); 
	Q->next = NULL;
	inQueue(s,Q);//顶点s入队,并访问 
	Graph->visited[s] = 1; 
	while(!isEmpty(Q)) 
	{	
		//若顶点与i与s存在边且未被访问,则入队且标记已访问
		for(int i = 0;i < Graph->nv;i++)
		{
			if(Graph->G[s][i] && !Graph->visited[i])
			{
				inQueue(i,Q);
				Graph->visited[i] = 1;
			}
		}
		s = deQueue(Q);//出队一个顶点且设为s,循环
		printf("%d ",s);
	}
}

void Dijkstra(MGraph Graph,int s)//各顶点到顶点s的最短路径 
{
	int min = 999;//用于寻找最短路径 
	int minV = -1;//用于存储最短路径的顶点索引 
	for(int i = 0;i < Graph->nv;i++)
	{	//初始化并后续更新Graph->dist数组中的最短路径 
		if(Graph->G[s][i] && !Graph->visited[i])
		{	 
			if(Graph->dist[s]+Graph->G[s][i] < Graph->dist[i])
			{
				Graph->path[i] = s;
				Graph->dist[i] = Graph->G[s][i] + Graph->dist[s];
			}
		}
		//寻找待收录顶点minV 
		if(Graph->dist[i] < min && !Graph->visited[i])
			{
				min = Graph->dist[i];
				minV = i;
			}
	}
	if(min == 999) return ;//若minV不存在,终止调用 
	Graph->visited[minV] = 1;

	Dijkstra(Graph,minV);//对新收录的顶点递归调用 
}

void prim(MGraph Graph,int s)//s为root的最小生成树 
{
	Graph->visited[s] = 1;//收录顶点s 
	
	int min = 999;//功能类似Dijkstra中的min与minV 
	int minV = -1;
	for(int i = 0;i < Graph->nv;i++)
	{
		//初始化与更新Graph->dis数组 
		if(Graph->G[s][i]&&!Graph->visited[i])
		{
			if(Graph->G[s][i] < Graph->dis[i])
			{
				Graph->dis[i] = Graph->G[s][i];
				Graph->primParent[i] = s;
			}
		}
		//寻找下一个待收录顶点 
		if(!Graph->visited[i])
		{
			if(Graph->dis[i] < min)
			{
				min = Graph->dis[i];
				minV = i;
			}
		}
	}
	if(min == 999) return ;//若minV不存在,终止调用 
	Graph->visited[minV] = 1;//收录minV且递归调用 
	prim(Graph,minV);
}

下面为测试使用的main函数以及测试数据

int main()
{
	MGraph Graph = NULL;
	BuildGraph(Graph);//创建一个图 
	for(int i = 0;i < Graph->nv;i++)//输出创建的图 
	{
		for(int j = 0;j < Graph->nv;j++)
		{
			printf("%d ",Graph->G[i][j]);
		}
		printf("\n");
	}
		printf("\n");
	 
	//初始化visited并DFS 
	initArray(Graph->visited,Graph->nv,0); 
	controlDFS(Graph);

	
	//初始化visited并BFS  
	initArray(Graph->visited,Graph->nv,0);
	BFS(Graph,0);
	printf("\n");

	
	//初始化visited、dist、path、并求出各顶点到顶点s的最短路径 
	int s = 0;
	scanf("%d",&s);
	initArray(Graph->visited,Graph->nv,0);
	initArray(Graph->dist,Graph->nv,999);
	initArray(Graph->path,Graph->nv,s);
	Graph->path[s] = -1;
	Graph->dist[s] = 0;
	Dijkstra(Graph,s);
	//输出调用Dijkstra函数的结果 
	for(int i = 0;i < Graph->nv;i++)
	{
		printf("%d to %d dist = %d,path:%d\n",s,i,Graph->dist[i],Graph->path[i]);
	} 
	printf("\n");	

	
	//初始化visited、primParent、dis,并求出以s为根节点的最小生成树 
	initArray(Graph->visited,Graph->nv,0);
	initArray(Graph->primParent,Graph->nv,s);
	initArray(Graph->dis,Graph->nv,999);
	Graph->primParent[s] = -1;
	prim(Graph,s);
	//输出调用Prim函数的结果 
	for(int i = 0;i < Graph->nv;i++)
	{
		printf("%d:parent = %d\n",i,Graph->primParent[i]);
	}
	
	return 0;
} 

测试数据如下

6 7
0 1 5
0 2 3
1 3 1
1 4 4
2 4 2
2 5 1
3 5 2
1

数据第一行表示6个顶点 7条边,最后一行为Dijkstra与Prim算法的开始顶点s

图 如下图

5
3
1
4
2
1
2
0
1
2
3
4
5

运行结果如下

0 5 3 0 0 0
5 0 0 1 4 0
3 0 0 0 2 1
0 1 0 0 0 2
0 4 2 0 0 0
0 0 1 2 0 0

0 1 3 5 2 4
0 1 2 3 4 5

1 to 0 dist = 5,path:1
1 to 1 dist = 0,path:-1
1 to 2 dist = 4,path:5
1 to 3 dist = 1,path:1
1 to 4 dist = 4,path:1
1 to 5 dist = 3,path:3

0:parent = 2
1:parent = -1
2:parent = 5
3:parent = 1
4:parent = 2
5:parent = 3

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值