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
图 如下图
运行结果如下
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