基于邻接矩阵的克鲁斯卡尔算法和普利姆算法

typedef struct {
    int vexs[5]; // 顶点
    int arcs[5][5]; //领接矩阵
    int vexnum, arcnum; //顶点数和边数
} AmGraph;
//region 克鲁斯卡尔算法
/**
 * 克鲁斯卡尔算法  最小生成树 以边为排序
 * 算法思想:
 *  将边按照权值进行升序排序,依次选择权值最小的、且不构成环的边
 * 算法步骤:
 *  1.需要借助辅助结构体数组,用于存储边的起始点start,和终点end 以及权值weight
 *  2.初始化边的数组(边数组),边数组的大小为边的大小。采用二维数组进行初始化
 *  3.将初始化完成之后的边数组,按照权值weight的大小进行升序排序
 *  4.初始化父节点数组parent(父数组),初始值设为-1,表示根节点。父数组的大小等于顶点的大小
 *  5.for循环边数组生成最小生成树
 *      ①:拿取当前节点的 起始节点start,终点节点end
 *      ②:在父数组中,分别找到这两个节点的根节点。采用while循环 =-1时说明找到根节点
 *      ③:如果两个节点的根节点不相同说明不存在环,输出结果。
 *      ④:合并根节点,将终点节点的父节点设为起始节点。(start为根节点) parent[end] = start
 *      重复上述步骤
 */
typedef struct {
    int start;
    int end;
    int weight;
} Edge;
// Kruskal算法需要的辅助函数,用于比较边的权值
int CompareEdges(const void *a, const void *b) {
    return ((Edge *) a)->weight - ((Edge *) b)->weight;
}
void kruskal(AmGraph *g) {
    Edge edges[g->arcnum]; // 存储边的数组
    int index = 0;

    // 初始化edges数组
    for (int i = 0; i < g->vexnum; ++i) {
        for (int j = i + 1; j < g->vexnum; ++j) {
            if (g->arcs[i][j] == INT_MAX) {
                continue;
            }
            if (g->arcs[i][j] != -1) {
                edges[index].start = i;
                edges[index].end = j;
                edges[index].weight = g->arcs[i][j];
                index++;
            }
        }
    }

    // 对边按权值升序排序
    qsort(edges, index, sizeof(Edge), CompareEdges);

    // 初始化并查集
    int parent[g->vexnum];
    for (int i = 0; i < g->vexnum; i++) {
        parent[i] = -1;
    }

    printf("Kruskal算法生成的最小生成树边集合:\n");
    for (int i = 0; i < index; i++) {
        int start = edges[i].start;
        int end = edges[i].end;

        // 查找两个顶点的根节点
        while (parent[start] != -1) { // 找到start的根节点  =-1时start就是根节点
            start = parent[start];
        }
        while (parent[end] != -1) { // 找到end的根节点
            end = parent[end];
        }

        // 如果两个顶点的根节点不相同,说明不构成环路
        if (start != end) {
            printf("(%d, %d) -> %d\n", edges[i].start, edges[i].end, edges[i].weight);
            parent[end] = start; // 合并,保留一个根节点
        }
    }
}
//endregion

//region 普利姆算法
/**
 * 普利姆算法  最小生成树 以点为排序
 * 算法思想:
 *  从起始节点开始,选择与起始节点连接的边最小的、且不构成环的节点,作为下一起始节点。
 * 算法步骤:
 *  b站,up主详细讲解
 *  https://www.bilibili.com/video/BV1Ua4y1i7tf/?spm_id_from=333.788&vd_source=99c6f1474e6726258fc34b2c9334a458
 *  1.初始化辅助数组
 *  2.起始节点设为已访问,即weight设为0
 *  3.for循环遍历,注意遍历次数-1,因为已经将起始节点标记为已访问
 *  4.寻找最短的邻接点
 *  5.输出结果
 *  6.将该点标记为已访问,即weight设为0
 *  7.更新辅助数组
 */
typedef struct {
    int adjvex; // 顶点序号
    int weight; // 权值  ,weight=0表示已经访问
} ShortArc; // 记录最短边
void Prim(AmGraph *g, int start) {
    ShortArc shortArc[g->vexnum];

    //初始化辅助数组
    for (int i = 0; i < g->vexnum; ++i) {
        shortArc[i].weight = g->arcs[start][i];
        shortArc[i].adjvex = start;
    }

    // 标记已经被访问过,加入结果集中
    shortArc[start].weight = 0;

    printf("Prim算法生成的最小生成树边集合:\n");
    for (int i = 0; i < g->vexnum - 1; i++) {
        int k = INT_MAX;
        int minadj;
        // 寻找最短边的邻接点 minadj
        for (int j = 0; j < g->vexnum; ++j) {
            if (shortArc[j].weight < k && shortArc[j].weight != 0) {
                minadj = j;
                k = shortArc[j].weight;
            }
        }

        // 输出最小生成树的路径
        printf("(%d,%d)=>%d\n", shortArc[minadj].adjvex, minadj, shortArc[minadj].weight);
        shortArc[minadj].weight = 0; //  标记已经被访问过。加入结果集中

        //调整shortArc数组
        for (int j = 0; j < g->vexnum; ++j) {
            if (g->arcs[minadj][j] < shortArc[j].weight) {
                shortArc[j].weight = g->arcs[minadj][j];
                shortArc[j].adjvex = minadj;
            }
        }
    }
}
//endregion
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值