编程:Kruskal算法和Prim算法的实现。C语言
时间: 2024-06-09 18:02:44 浏览: 125
cpp-图论算法最小生成树Prim算法和Kruskal算法C实现
5星 · 资源好评率100%
Kruskal算法和Prim算法都是用于求解最小生成树的经典算法,下面我会为你介绍它们的实现。
Kruskal算法:
Kruskal算法通过不断添加边来构建最小生成树,具体步骤如下:
1. 将所有边按照边权值从小到大排序
2. 从小到大遍历每条边,如果该边连接的两个点不在同一个连通块中,则将该边加入最小生成树中,并将这两个点合并为一个连通块
3. 重复步骤2,直到最小生成树中包含了n-1条边(n为节点数)
下面是Kruskal算法的C语言实现:
```c
#include<stdio.h>
#include<stdlib.h>
#define MAXN 1000
#define MAXM 100000
typedef struct edge {
int u, v, w;
}Edge;
Edge E[MAXM];
int fa[MAXN], n, m;
int cmp(const void *a, const void *b) { // 边的比较函数,按照边权值从小到大排序
return ((Edge*)a)->w - ((Edge*)b)->w;
}
int find(int x) { // 并查集查找根节点函数
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
int kruskal() { // Kruskal算法主体
int ans = 0, cnt = 0;
for (int i = 1; i <= n; i++) fa[i] = i; // 初始化并查集,每个节点的父亲都是自己
qsort(E, m, sizeof(Edge), cmp); // 对所有边按照边权值从小到大排序
for (int i = 0; i < m; i++) {
int u = E[i].u, v = E[i].v, w = E[i].w;
int fau = find(u), fav = find(v);
if (fau != fav) { // 如果u和v不在同一个连通块中,则将其合并为一个连通块,并将该边加入最小生成树中
fa[fau] = fav;
ans += w;
cnt++;
if (cnt == n - 1) break; // 如果加入的边数达到n-1,则说明最小生成树已经构建完成
}
}
if (cnt < n - 1) return -1; // 如果最小生成树的边数不足n-1,则图不连通
return ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
}
int ans = kruskal();
if (ans == -1) printf("图不连通");
else printf("最小生成树的权值和为%d", ans);
return 0;
}
```
Prim算法:
Prim算法通过维护一个优先队列来构建最小生成树,具体步骤如下:
1. 随意选择一个节点作为起点,并将其加入最小生成树中
2. 将与该节点相邻的所有边加入优先队列中
3. 从优先队列中取出一条边,如果该边连接的另一个节点不在最小生成树中,则将该节点加入最小生成树中,并将与该节点相邻的所有边加入优先队列中
4. 重复步骤3,直到最小生成树中包含了n-1条边(n为节点数)
下面是Prim算法的C语言实现:
```c
#include<stdio.h>
#include<stdlib.h>
#define MAXN 1000
#define MAXM 100000
typedef struct edge {
int v, w;
}Edge;
Edge G[MAXN][MAXN];
int vis[MAXN], dis[MAXN], n, m;
void prim(int s) { // Prim算法主体
for (int i = 1; i <= n; i++) dis[i] = G[s][i].w; // 初始化dis数组,表示s到每个节点的最短距离
for (int i = 1; i < n; i++) { // 最小生成树有n-1条边
int u = -1;
for (int j = 1; j <= n; j++) { // 找到一个未访问过的与s距离最短的节点u
if (!vis[j] && (u == -1 || dis[j] < dis[u])) u = j;
}
vis[u] = 1; // 标记u已经被访问过了
for (int v = 1; v <= n; v++) { // 更新dis数组
if (!vis[v] && G[u][v].w < dis[v]) dis[v] = G[u][v].w;
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
G[u][v].v = G[v][u].v = u; // 记录每条边的起点和终点
G[u][v].w = G[v][u].w = w; // 记录每条边的边权值
}
int s = 1; // 假设起点为1号节点
vis[s] = 1; // 标记起点已经被访问过了
prim(s);
int ans = 0;
for (int i = 1; i <= n; i++) ans += dis[i];
printf("最小生成树的权值和为%d", ans);
return 0;
}
```
阅读全文