代码实现都是基于这个b站中视频的讲解
prim算法
1.初始化每一个结点和图
2.从一个结点开始search
(1)update 将以此结点开始的边加入,如果更小进行替换,父节点也要替换。
(2)scan 扫描找到最小的边, 已经选择了的点不考虑,这个新的结点进行递归。
(3)add 将这个结点变为已选择过。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int **graphics;
int n;
int sign;
typedef struct node {
int number; //编号
bool select; //是否已经被选择过
int mindist; //最短的路径权值
int parent; //父亲结点
}node;
node *nodes;
//所有的初始化操作
void init() {
sign = 0;
printf("请输入最大结点数值");
scanf("%d", &n);
n = n + 1;
graphics = (int**)malloc(sizeof(int*) * n);
for (int i = 0; i < n; ++i) {
graphics[i] = (int*)malloc(sizeof(int) * n);
memset(graphics[i], 0, sizeof(int) * n);
}
nodes = (node*)malloc(sizeof(node) * n);
for (int i = 0; i < n; ++i) {
nodes[i].number = i;
nodes[i].select = false;
nodes[i].parent = -1;
nodes[i].mindist = INT_MAX;
}
printf("请输入边的数量");
int edgenumber;
scanf("%d", &edgenumber);
for (int i = 0; i < edgenumber; ++i) {
printf("请输入第%d条边的第一个结点的序号", i + 1);
int x;
scanf("%d", &x);
printf("请输入第%d条边的第二个结点的序号", i + 1);
int y;
scanf("%d", &y);
int val;
printf("请输入第%d条边的权值", i + 1);
scanf("%d", &val);
graphics[x][y] = val;
graphics[y][x] = val;
}
}
void search(int start) {
if (sign >= n - 1) return;
nodes[start].select = true;
//update
for (int i = 0; i < n; ++i) {
if (graphics[start][i] != 0) {
if (nodes[i].select == false) {//只能考虑没有选择过的结点
if (nodes[i].mindist > graphics[start][i]) {
nodes[i].mindist = graphics[start][i];
nodes[i].parent = start;
}
}
}
}
//scan
int minsign = INT_MAX;
int minnode;
for (int i = 0; i < n; ++i) {
if (nodes[i].select == false) {
if (nodes[i].mindist < minsign) {
minsign = nodes[i].mindist;
minnode = i;
}
}
}
//add
printf("连接%d-%d\n", nodes[minnode].parent, minnode);
nodes[minnode].select = true;
++sign;
search(minnode);
}
int main() {
init();
search(0);
return 0;
}
prim算法的时间复杂度为O(n^2)(来自于search中递归n此和for循环n次)
kruskal算法
1.对边的权值从小到大排序排序
2.加入集合判断是否构成环,如果构成环则加入这条边否在跳过这条边
3.当选择边的数目等于结点数-1则结束算法输出选择的边。
#include<stdio.h>
#include<stdlib.h>
//建立边结构
typedef struct edge {
int v1;
int v2;
int val;
bool select;
} edge;
edge *edges; //存储边信息
int *vertexs;//存储结点父节点
int vertex_number;//结点数量
int edge_number;//边数量
//初始化结点信息
void initvertex() {
printf("请输入结点数");
scanf("%d", &vertex_number);
vertexs = (int*)malloc(sizeof(int) * vertex_number);
for (int i = 0; i < vertex_number; ++i) {
vertexs[i] = i;
}
}
//初始化边的信息
void initedge() {
printf("请输入边的数");
scanf("%d", &edge_number);
edges = (edge*)malloc(sizeof(edge) * edge_number);
for (int i = 0; i < edge_number; ++i) {
printf("请输入第一个点的值");
scanf("%d", &edges[i].v1);
printf("请输入第二个点的值");
scanf("%d", &edges[i].v2);
printf("请输入第%d条边的值", i + 1);
scanf("%d", &edges[i].val);
edges[i].select = false;
}
}
//寻找祖先结点并压缩路径
int find(int x) {
int r = vertexs[x];
while (r != vertexs[r]) {
r = vertexs[r];
}
int i = x, j;
while (i != r) {
j = vertexs[i];
vertexs[i] = r;
i = j;
}
return r;
}
//合并两个结点
void merge(int x, int y) {
int xp = find(x);
int yp = find(y);
vertexs[xp] = yp;
}
int comp(const void *_a, const void *_b) {
edge a = *(edge*)_a;
edge b = *(edge*)_b;
return a.val - b.val;
}
//先排序然后从最小边开始判断是否有环如果没环则加入集合并进行下一条边的判断
void kruskal() {
initvertex();
initedge();
qsort(edges, edge_number, sizeof(edge), comp);
int sign = 0;
for (int i = 0; i < edge_number && sign != vertex_number - 1; ++i) {
if (find(edges[i].v1) != find(edges[i].v2)) {
merge(edges[i].v1, edges[i].v2);
edges[i].select = true;
++sign;
}
}
}
//输出选择的边
void show() {
int minsum;
for (int i = 0; i < edge_number; ++i) {
if (edges[i].select) {
minsum += edges[i].val;
printf("选择的边为%d-%d\n", edges[i].v1, edges[i].v2);
}
}
printf("最小的权值和为%d", minsum);
}
int main() {
kruskal();
show();
return 0;
}
kruscal的时间复杂度为O(n*logn)(取决于排序的时间复杂度)