首页 > 试题广场 >

最小生成树

[编程题]最小生成树
  • 热度指数:18895 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
一个有 n 户人家的村庄,有 m 条路相互连接着。村里现在要修路,每条路都有一个成本价格,现在请你帮忙计算下,最少需要花费多少钱,就能让这 n 户人家连接起来。

为一个二维数组,每个元素是一个长度为 3 的一维数组 表示村庄 和村庄 有一条路,修这条路的成本价格为  

每户之间可能有多条道路连接,但不可能自己与自己相连

数据范围:  ,  , 
进阶: 时间复杂度 , 空间复杂度
示例1

输入

3,3,[[1,3,3],[1,2,1],[2,3,1]]

输出

2
示例2

输入

2,1,[[1,2,1]]

输出

1
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 返回最小的花费代价使得这n户人家连接起来
 * @param n int整型 n户人家的村庄
 * @param m int整型 m条路
 * @param cost int整型二维数组 一维3个参数,表示连接1个村庄到另外1个村庄的花费的代价
 * @param costRowLen int cost数组行数
 * @param costColLen int* cost数组列数
 * @return int整型
 */
int miniSpanningTree(int n, int m, int** cost, int costRowLen,
                     int* costColLen) {
    // 定义一个数组来存储每个顶点到已加入生成树的最近顶点的距离
    int homevexcost[n];
    // 定义一个邻接矩阵来存储各顶点之间的边的权重
    int edgecost[n][n];
    // 初始化最小生成树的总成本为0
    int sumcost = 0;
    int u = 0;
    int v = 0;
    int w = 0;

    // 初始化邻接矩阵中未连接的顶点间的权重为极大值(这里用32767表示)
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            edgecost[i][j] = 32765;
        }
    }

    // 根据给定的边和成本信息填充邻接矩阵
    for (int i = 0; i < costRowLen; i++) {
        u = cost[i][0] - 1;
        v = cost[i][1] - 1;
        w = cost[i][2];
        edgecost[u][v] = w;
        edgecost[v][u] = w; // 因为图是无向的
    }

    // 初始化第一个顶点加入生成树的成本为0
    homevexcost[0] = 0;
    // 计算从第一个顶点出发到其他顶点的最短距离
    int k = 0;
    for (int i = 0; i < n; i++) {
        homevexcost[i] = edgecost[k][i];
    }

    // 主循环来构建最小生成树
    for (int i = 1; i < n; i++) {
        // 寻找离已加入生成树的顶点集合最近的顶点及其成本
        int mincost = 32765;
        int l;
        for (int j = 0; j < n; j++) {
            if (homevexcost[j] != 0 && homevexcost[j] < mincost) {
                mincost = homevexcost[j];
                l = j;
            }
        }

        // 更新已加入生成树的顶点集合,并累加当前边的成本到总成本中
        k = l;
        sumcost += mincost;
        homevexcost[l] = 0;

        // 更新从新加入的顶点出发到其他顶点的距离
        for (int a = 0; a < n; a++) {
            if (edgecost[k][a] < homevexcost[a]) {
                homevexcost[a] = edgecost[k][a];
            }
        }
    }

    // 返回最小生成树的总成本
    return sumcost;
}

起初写的时候只考虑了往返权重一样,调式的时候用于生成网的邻接矩阵没有正确的赋值,赋值03 30 的时候11也赋值了,提交测试这个6,10,[[5,3,8],[1,3,6],[2,5,4],[2,3,5],[4,5,6],[3,4,3],[2,4,8],[1,2,2],[1,4,5],[5,6,2]]
案例时赋值时第一次循环没有赋值,后面几次循环的赋值对应的权重也不对。恳请各位大佬解救一下
发表于 2024-08-25 17:01:58 回复(1)
c代码实现,主要影响是需要手写排序算法。
使用递归实现排序算法,可能导致栈溢出,使用栈实现要尽量加快循环遍历。
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 返回最小的花费代价使得这n户人家连接起来
 * @param n int整型 n户人家的村庄
 * @param m int整型 m条路
 * @param cost int整型二维数组 一维3个参数,表示连接1个村庄到另外1个村庄的花费的代价
 * @param costRowLen int cost数组行数
 * @param costColLen int* cost数组列数
 * @return int整型
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void swap(int* x, int* y) {
    int z[3] = {0};
    memcpy(z, x, 3 * sizeof(int));
    memcpy(x, y, 3 * sizeof(int));
    memcpy(y, z, 3 * sizeof(int));
}

typedef struct _Range {
    int start, end;
} Range;

Range new_Range(int s, int e) {
    Range r;
    r.start = s;
    r.end = e;
    return r;
}

void quick_sort(int* arr[], const int len) {
    if (len <= 0)
        return; //避免len等于负值时错误
    //r[]模拟堆疊,p为数量,r[p++]为push,r[--p]为pop且取得元素
    Range r[len];
    int p = 0;
    r[p++] = new_Range(0, len - 1);
    while (p) {
        Range range = r[--p];
        if (range.start >= range.end)
            continue;
        int mid = arr[range.end][2];
        int left = range.start, right = range.end - 1;
        while (left < right) {
            while (arr[left][2] < mid && left < right)
                left++;
            while (arr[right][2] >= mid && left < right)
                right--;
            if(left<right){
                swap(arr[left], arr[right]);
                //加速设置,使代码尽快遍历完,否则超时
                left++;
                right--;
            }
        }
        if (arr[left][2] > arr[range.end][2])
            swap(arr[left], arr[range.end]);
        else
            left++;
        r[p++] = new_Range(range.start, left - 1);
        r[p++] = new_Range(left + 1, range.end);
    }
}

int find(int* parent, int x) { //找到最高的父亲节点
    if (parent[x] != x) {
        parent[x] = find(parent, parent[x]);
    }
    return parent[x];
}

int miniSpanningTree(int n, int m, int** cost, int costRowLen,
                     int* costColLen ) {
    // write code here
    int* parent = (int*)malloc((n+10) * sizeof(int));
    for (int i = 1; i <=n; i++) { //初始父亲设置为自己
        parent[i] = i;
    }
    quick_sort(cost, costRowLen);//按边递增排序

    int res = 0;
    for (int i = 0; i < costRowLen;
            i++) { //遍历所有边,连通的放入一个并查集
        int x = cost[i][0];
        int y = cost[i][1];
        int z = cost[i][2];
        int px = find(parent, x); //查找最上边父亲
        int py = find(parent, y);
        if (px != py) { //如果二者不在同一集合
            res += z;
            parent[px] = py; //设置最上边父亲相同
        }
    }
    return res;
}

发表于 2023-07-20 15:03:38 回复(0)
prim算法用例5超时了
5000个点,50000条路径
在dev上跑了下和预期输出相符
怎么优化

发表于 2022-02-20 20:15:49 回复(1)