当前位置: 首页 > news >正文

最小生成树算法的相关变形题

由于吉大近几年的考试中,算法大题里频繁地出现最小生成树算法的变形题,因此在此整理一下。

PS:prim算法不管是画图还是代码题真的难用,kruskal算法才是永远滴神

目录

最小生成树

最大生成树

加入一条新边,求新的最小生成树

次小生成树

判断最小生成树是否唯一


最小生成树

#define maxsize 100

//并查集
int father[maxsize] = { 0 };
int Find(int x)
{
    if (father[x] <= 0) return x;
    return father[x] = Find(father[x]);
}
void Union(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy) return;
    if (father[fx] < father[fy])
    {
        father[fy] = fx;
    }
    else
    {
        if (father[fx] == father[fy])
        {
            father[fy]--;
        }
        father[fx] = fy;
    }
}
//对边进行排序,并存入edge数组中
int sort_edge(int graph[][6], int n, int edge[][3])
{
    int edgenum = 0;  //k为边的数量
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            if (graph[i][j])
            {
                edgenum++;
                edge[edgenum][0] = i;
                edge[edgenum][1] = j;
                edge[edgenum][2] = graph[i][j];
            }
        }
    }
    for (int i = edgenum; i > 1; i--)
    {
        int flag = 0;
        for (int j = 1; j < i; j++)
        {
            if (edge[j][2] > edge[j + 1][2])
            {
                flag = 1;

                int temp = edge[j][0];
                edge[j][0] = edge[j + 1][0];
                edge[j + 1][0] = temp;

                temp = edge[j][1];
                edge[j][1] = edge[j + 1][1];
                edge[j + 1][1] = temp;

                temp = edge[j][2];
                edge[j][2] = edge[j + 1][2];
                edge[j + 1][2] = temp;
            }
        }
        if (!flag) break;
    }
    printf("排序后的边为:");
    for (int i = 1; i <= edgenum; i++)
    {
        printf("%d--%d:%d  ", edge[i][0], edge[i][1], edge[i][2]);
    }printf("\n\n");
    return edgenum;
}
//kruskal算法
void kruskal(int graph[][6], int n)
{
    int edge[maxsize][3];
    int edgenum = sort_edge(graph, n, edge);
    int T = n;    //集合的个数
    int k = 1;
    int ans = 0;
    printf("最小生成树:");
    while (T > 1)
    {
        int a = edge[k][0];
        int b = edge[k][1];
        int w = edge[k][2];
        k++;

        if (Find(a) == Find(b)) continue;
        Union(a, b);
        T--;
        ans += w;
        printf("%d--%d:%d  ", a, b, w);
    }printf("\n\n");
    printf("最小生成树的权值为:%d\n", ans);
}

int main()
{
    int graph[6][6] = { 0 };
    int n = 5;
    graph[1][4] = 1;    graph[4][1] = 1;
    graph[1][3] = 2;    graph[3][1] = 2;
    graph[1][2] = 3;    graph[2][1] = 3;
    graph[3][4] = 4;    graph[4][3] = 4;
    graph[4][5] = 5;    graph[5][4] = 5;
    graph[2][5] = 6;    graph[5][2] = 6;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%d ", graph[i][j]);
        }printf("\n");
    }printf("\n");

    kruskal(graph, n);
    return 0;
}

对于下图来说:

                                

其最小生成树为:

                        ​​​​​​​        ​​​​​​​      

上述代码的结果为:

 

最大生成树

最简单的一个,就是把一开始的边排序改为降序排序而已,和上面的代码相比就是改了个大于号,其他的都一样

#define maxsize 100

int father[maxsize] = { 0 };
int Find(int x)
{
    if (father[x] <= 0) return x;
    return father[x] = Find(father[x]);
}
void Union(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy) return;
    if (father[fx] < father[fy])
    {
        father[fy] = fx;
    }
    else
    {
        if (father[fx] == father[fy])
        {
            father[fy]--;
        }
        father[fx] = fy;
    }
}
//对边进行排序,并存入edge数组中
int sort_edge(int graph[][6], int n, int edge[][3])
{
    int edgenum = 0;  //k为边的数量
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            if (graph[i][j])
            {
                edgenum++;
                edge[edgenum][0] = i;
                edge[edgenum][1] = j;
                edge[edgenum][2] = graph[i][j];
            }
        }
    }
    for (int i = edgenum; i > 1; i--)
    {
        int flag = 0;
        for (int j = 1; j < i; j++)
        {
            if (edge[j][2] < edge[j + 1][2])    //这里改为降序排序
            {
                flag = 1;

                int temp = edge[j][0];
                edge[j][0] = edge[j + 1][0];
                edge[j + 1][0] = temp;

                temp = edge[j][1];
                edge[j][1] = edge[j + 1][1];
                edge[j + 1][1] = temp;

                temp = edge[j][2];
                edge[j][2] = edge[j + 1][2];
                edge[j + 1][2] = temp;
            }
        }
        if (!flag) break;
    }
    printf("排序后的边为:");
    for (int i = 1; i <= edgenum; i++)
    {
        printf("%d--%d:%d  ", edge[i][0], edge[i][1], edge[i][2]);
    }printf("\n\n");
    return edgenum;
}

void kruskal(int graph[][6], int n)
{
    int edge[maxsize][3];
    int edgenum = sort_edge(graph, n, edge);
    int T = n;    //集合的个数
    int k = 1;
    int ans = 0;
    printf("最小生成树:");
    while (T > 1)
    {
        int a = edge[k][0];
        int b = edge[k][1];
        int w = edge[k][2];
        k++;

        if (Find(a) == Find(b)) continue;
        Union(a, b);
        T--;
        ans += w;
        printf("%d--%d:%d  ", a, b, w);
    }printf("\n\n");
    printf("最小生成树的权值为:%d\n", ans);
}

int main()
{
    int graph[6][6] = { 0 };
    int n = 5;
    graph[1][4] = 1;    graph[4][1] = 1;
    graph[1][3] = 2;    graph[3][1] = 2;
    graph[1][2] = 3;    graph[2][1] = 3;
    graph[3][4] = 4;    graph[4][3] = 4;
    graph[4][5] = 5;    graph[5][4] = 5;
    graph[2][5] = 6;    graph[5][2] = 6;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%d ", graph[i][j]);
        }printf("\n");
    }printf("\n");

    kruskal(graph, n);
    return 0;
}

 对于下图来说:

                ​​​​​​​        ​​​​​​​        

其最大生成树为:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​      

代码的结果为:

加入一条新边,求新的最小生成树

假设图G的最小生成树T已知,现给定一条加入G的新边e(G的点集不变),给出寻找G+e新图的最小生成树算法,时间复杂度O(n),n为G的顶点数,G/T采用邻接表/矩阵存储即可。

大致思路:在最小生成树中加入一条新边 newa -- newb(权值为neww),肯定会构成一个环,然后找到环中除了新边 newa -- newb 之外最长的一条边的权值,也就是在原先的最小生成树中从节点newa到节点newb的路径上最长的一条边的权值maxw,如果新边的权值neww < maxw,那么我们就可以用这条新边代替原先的最长边,新的最小生成树的权值自然也会变小;如果neww >= maxw,那么不用替代,原先的最小生成树依旧是新的最小生成树。

实际上就是在原先的kruskal算法的基础上增加了两个函数:

int mintree[maxsize][maxsize] = { 0 };        //用邻接矩阵的方式存储最小生成树
int n = 5;
//用dfs找到形成的环中,除新加入的边 a-b 外,最长的一条边的权值
//即在原先的生成树中,从节点a到节点b的路径上最长的一条边的权值
void dfs(int v, int b, int visited[], int curw, int* maxw)      //curw是从节点a到当前节点v的路径上最长的一条边的权值
{
    visited[v] = 1;
    if (v == b)     //如果找到了b节点
    {
        *maxw = curw;
        return;
    }
    for (int i = 1; i <= n; i++)
    {
        if (mintree[v][i] != 0 && !visited[i])
        {
            int temp = curw;    //记录一下原先curw的值,用于回溯
            if (mintree[v][i] > curw) curw = mintree[v][i];
            dfs(i, b, visited, curw, maxw);
            curw = temp;        //回溯
        }
    }
}
//向图中加入一条新边后,求最小生成树
void Add_newedge(int newa, int newb, int neww, int ans)
{
    int visited[maxsize] = { 0 };
    int maxw;    //存储最长边的权值
    dfs(newa, newb, visited, 0, &maxw);
    printf("在原先的最小生成树中,节点%d到节点%d的路径上的最长边的权值为:%d\n\n", newa, newb, maxw);
    if (neww < maxw)    //如果新加入的这条边的权值小于最长边的权值,那么就能够用这题新边代替原来的最长边
    {
        ans = ans - maxw + neww;
    }
    printf("加入边 %d--%d 后,新的最小生成树的权值为:%d\n",newa, newb, ans);
}

完整的代码如下:

#define maxsize 100

int father[maxsize] = { 0 };
int Find(int x)
{
    if (father[x] <= 0) return x;
    return father[x] = Find(father[x]);
}
void Union(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy) return;
    if (father[fx] < father[fy])
    {
        father[fy] = fx;
    }
    else
    {
        if (father[fx] == father[fy])
        {
            father[fy]--;
        }
        father[fx] = fy;
    }
}
//对边进行排序,并存入edge数组中
int sort_edge(int graph[][6], int n, int edge[][3])
{
    int edgenum = 0;  //k为边的数量
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            if (graph[i][j])
            {
                edgenum++;
                edge[edgenum][0] = i;
                edge[edgenum][1] = j;
                edge[edgenum][2] = graph[i][j];
            }
        }
    }
    for (int i = edgenum; i > 1; i--)
    {
        int flag = 0;
        for (int j = 1; j < i; j++)
        {
            if (edge[j][2] > edge[j + 1][2])
            {
                flag = 1;

                int temp = edge[j][0];
                edge[j][0] = edge[j + 1][0];
                edge[j + 1][0] = temp;

                temp = edge[j][1];
                edge[j][1] = edge[j + 1][1];
                edge[j + 1][1] = temp;

                temp = edge[j][2];
                edge[j][2] = edge[j + 1][2];
                edge[j + 1][2] = temp;
            }
        }
        if (!flag) break;
    }
    printf("排序后的边为:");
    for (int i = 1; i <= edgenum; i++)
    {
        printf("%d--%d:%d  ", edge[i][0], edge[i][1], edge[i][2]);
    }printf("\n\n");
    return edgenum;
}

int mintree[maxsize][maxsize] = { 0 };        //用邻接矩阵的方式存储最小生成树
int n = 5;
//找到形成的环中,除新加入的边 a-b 外,最长的一条边的权值
//即在原先的生成树中,从节点a到节点b的路径上最长的一条边的权值
void dfs(int v, int b, int visited[], int curw, int* maxw)      //curw是从节点a到当前节点v的路径上最长的一条边的权值
{
    visited[v] = 1;
    if (v == b)     //如果找到了b节点
    {
        *maxw = curw;
        return;
    }
    for (int i = 1; i <= n; i++)
    {
        if (mintree[v][i] != 0 && !visited[i])
        {
            int temp = curw;    //记录一下原先curw的值,用于回溯
            if (mintree[v][i] > curw) curw = mintree[v][i];
            dfs(i, b, visited, curw, maxw);
            curw = temp;        //回溯
        }
    }
}
//向图中加入一条新边后,求最小生成树
void Add_newedge(int newa, int newb, int neww, int ans)
{
    int visited[maxsize] = { 0 };
    int maxw;    //存储最长边的权值
    dfs(newa, newb, visited, 0, &maxw);
    printf("在原先的最小生成树中,节点%d到节点%d的路径上的最长边的权值为:%d\n\n", newa, newb, maxw);
    if (neww < maxw)    //如果新加入的这条边的权值小于最长边的权值,那么就能够用这题新边代替原来的最长边
    {
        ans = ans - maxw + neww;
    }
    printf("加入边 %d--%d 后,新的最小生成树的权值为:%d\n",newa, newb, ans);
}

int kruskal(int graph[][6], int n)
{
    int edge[maxsize][3];
    int edgenum = sort_edge(graph, n, edge);
    int T = n;    //集合的个数
    int k = 1;
    int ans = 0;
    printf("最小生成树:");
    while (T > 1)
    {
        int a = edge[k][0];
        int b = edge[k][1];
        int w = edge[k][2];
        k++;

        if (Find(a) == Find(b)) continue;
        Union(a, b);
        T--;
        ans += w;
        mintree[a][b] = mintree[b][a] = w;
        printf("%d--%d:%d  ", a, b, w);
    }printf("\n\n");
    printf("最小生成树的权值为:%d\n\n", ans);
    return ans;
}

int main()
{
    int graph[6][6] = { 0 };
    int n = 5;
    graph[1][4] = 1;    graph[4][1] = 1;
    graph[1][3] = 2;    graph[3][1] = 2;
    graph[1][2] = 3;    graph[2][1] = 3;
    graph[3][4] = 4;    graph[4][3] = 4;
    graph[4][5] = 5;    graph[5][4] = 5;
    graph[2][5] = 6;    graph[5][2] = 6;
    printf("原图:\n");
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%d ", graph[i][j]);
        }printf("\n");
    }printf("\n");

    int ans = kruskal(graph, n);

    printf("--------------------------------------------------------------------------\n\n");
    printf("最小生成树的邻接矩阵表示:\n");
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%d ", mintree[i][j]);
        }printf("\n");
    }printf("\n");

    printf("加入一条新边 2--4,权值为1\n\n");
    //加入一条新边 2-4 ,权值为1
    Add_newedge(2, 4, 1, ans);
    return 0; 
}

对于下图来说:

加入一条新边后:

 上述代码的打印结果为:

 

次小生成树

只要会了 “加入一条新边,求新的最小生成树” 这题的话,求次小生成树就不难了,思路基本一致,只是稍微改动一些地方。

大致思路:先求得最小生成树,然后枚举图中未加入最小生成树的各条边, 在最小生成树中加入一条新边 newa -- newb(权值为neww),肯定会构成一个环,然后找到环中除了新边 newa -- newb 之外最长的一条边的权值,也就是在原先的最小生成树中从节点newa到节点newb的路径上最长的一条边的权值maxw,我们就可以直接用这条新边代替原先的最长边(注意,如果求的是严格次小的话,需要比较neww和maxw的大小,只有neww ≠ maxw时才用新边代替原先的最长边),得到一棵新的生成树;在所有的新生成树中最小的那棵,就是次小生成树。

在main函数中枚举未加入最小生成树的边,调用Add_edge函数:

    int newans = 999;
    for (int i = 1; i <= edgenum; i++)
    {
        if (!isuse[i])    //枚举未加入最小生成树的边
        {
            int tmp = Add_newedge(edge[i][0], edge[i][1], edge[i][2], ans);
            if (newans > tmp) newans = tmp;
        }
    }
    printf("次小生成树的权值为:%d\n", newans);

完整的代码如下:

#define maxsize 100

int father[maxsize] = { 0 };
int Find(int x)
{
    if (father[x] <= 0) return x;
    return father[x] = Find(father[x]);
}
void Union(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy) return;
    if (father[fx] < father[fy])
    {
        father[fy] = fx;
    }
    else
    {
        if (father[fx] == father[fy])
        {
            father[fy]--;
        }
        father[fx] = fy;
    }
}
//对边进行排序,并存入edge数组中
int sort_edge(int graph[][6], int n, int edge[][3])
{
    int edgenum = 0;  //k为边的数量
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            if (graph[i][j])
            {
                edgenum++;
                edge[edgenum][0] = i;
                edge[edgenum][1] = j;
                edge[edgenum][2] = graph[i][j];
            }
        }
    }
    for (int i = edgenum; i > 1; i--)
    {
        int flag = 0;
        for (int j = 1; j < i; j++)
        {
            if (edge[j][2] > edge[j + 1][2])
            {
                flag = 1;

                int temp = edge[j][0];
                edge[j][0] = edge[j + 1][0];
                edge[j + 1][0] = temp;

                temp = edge[j][1];
                edge[j][1] = edge[j + 1][1];
                edge[j + 1][1] = temp;

                temp = edge[j][2];
                edge[j][2] = edge[j + 1][2];
                edge[j + 1][2] = temp;
            }
        }
        if (!flag) break;
    }
    printf("排序后的边为:");
    for (int i = 1; i <= edgenum; i++)
    {
        printf("%d--%d:%d  ", edge[i][0], edge[i][1], edge[i][2]);
    }printf("\n\n");
    return edgenum;
}

int mintree[maxsize][maxsize] = { 0 };        //用邻接矩阵的方式存储最小生成树
int n = 5;
//找到形成的环中,除新加入的边 a-b 外,最长的一条边的权值
//即在原先的生成树中,从节点a到节点b的路径上最长的一条边的权值
void dfs(int v, int b, int visited[], int curw, int* maxw)      //curw是从节点a到当前节点v的路径上最长的一条边的权值
{
    visited[v] = 1;
    if (v == b)     //如果找到了b节点
    {
        *maxw = curw;
        return;
    }
    for (int i = 1; i <= n; i++)
    {
        if (mintree[v][i] != 0 && !visited[i])
        {
            int temp = curw;    //记录一下原先curw的值,用于回溯
            if (mintree[v][i] > curw) curw = mintree[v][i];
            dfs(i, b, visited, curw, maxw);
            curw = temp;        //回溯
        }
    }
}
//向图中加入一条新边后,求最小生成树
int Add_newedge(int newa, int newb, int neww, int ans)
{
    int visited[maxsize] = { 0 };
    int maxw;    //存储最长边的权值
    dfs(newa, newb, visited, 0, &maxw);
    printf("在原先的最小生成树中,节点%d到节点%d的路径上的最长边的权值为:%d\n", newa, newb, maxw);
    ans = ans - maxw + neww;
    printf("加入边 %d--%d 后,新的最小生成树的权值为:%d\n\n", newa, newb, ans);
    return ans;
}

int edge[maxsize][3];       //存储边的数组
int edgenum;                //边数
int isuse[maxsize] = { 0 }; //标记某个边是否被用来加入最小生成树
int kruskal(int graph[][6], int n)
{
    edgenum = sort_edge(graph, n, edge);
    int T = n;    //集合的个数
    int k = 1;
    int ans = 0;
    printf("最小生成树:");
    while (T > 1)
    {
        int a = edge[k][0];
        int b = edge[k][1];
        int w = edge[k][2];
        k++;

        if (Find(a) == Find(b)) continue;
        Union(a, b);
        T--;
        ans += w;
        mintree[a][b] = mintree[b][a] = w;
        isuse[k - 1] = 1;
        printf("%d--%d:%d  ", a, b, w);
    }printf("\n\n");
    printf("最小生成树的权值为:%d\n\n", ans);
    return ans;
}

int main()
{
    int graph[6][6] = { 0 };
    int n = 5;
    graph[1][4] = 1;    graph[4][1] = 1;
    graph[1][3] = 2;    graph[3][1] = 2;
    graph[1][2] = 3;    graph[2][1] = 3;
    graph[3][4] = 4;    graph[4][3] = 4;
    graph[4][5] = 5;    graph[5][4] = 5;
    graph[2][5] = 6;    graph[5][2] = 6;
    printf("原图:\n");
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%d ", graph[i][j]);
        }printf("\n");
    }printf("\n");

    int ans = kruskal(graph, n);

    printf("--------------------------------------------------------------------------\n\n");
    printf("最小生成树的邻接矩阵表示:\n");
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%d ", mintree[i][j]);
        }printf("\n");
    }printf("\n");

    int newans = 999;
    for (int i = 1; i <= edgenum; i++)
    {
        if (!isuse[i])    //枚举未加入最小生成树的边
        {
            int tmp = Add_newedge(edge[i][0], edge[i][1], edge[i][2], ans);
            if (newans > tmp) newans = tmp;
        }
    }
    printf("次小生成树的权值为:%d\n", newans);

    return 0;
}

对于下图来说:

其次小生成树为:

 ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

上述的代码的打印结果为:

判断最小生成树是否唯一

可以用求次小生成树的办法,如果次小生成树的权值与最小生成树的权值相同,就说明最小生成树不唯一,但是这题倒不用这么复杂。

原理: 如果无向图中各边的权值各不相同,那么用Kruskal算法构造的最小生成树是惟一的,因此在无向图中存在相同权值的边是最小生成树不唯一的必要非充分条件。

大致思路:在使用Kruskal算法时,如果有两条边的权值相同,并且都能将两个相同的连通块合并,那么这两条边选择哪个都行,于是最小生成树就不唯一了。

只要在kruskal算法的基础上,改一下kruskal函数即可:

//kruskal算法
void kruskal(int graph[][6], int n)
{
    int edge[maxsize][3];
    int edgenum = sort_edge(graph, n, edge);
    int T = n;    //集合的个数
    int k = 1;
    int ans = 0;
    //printf("最小生成树:");
    while (T > 1)
    {
        int a = edge[k][0];
        int b = edge[k][1];
        int w = edge[k][2];
        k++;

        if (Find(a) == Find(b)) continue;
        //由于边是降序排序的,因此可以往后查找与当前的边权值相同的边
        for (int i = k; i <= edgenum; i++)
        {
            if (edge[i][2] == edge[k][2])
            {
                int newa = edge[i][0];
                int newb = edge[i][1];
                //如果 a--b 和 newa--newb 这两边权值相同,并且所处的两个连通块都一样,就说明最小生成树不唯一
                if (Find(a) == Find(newa) && Find(b) == Find(newb))
                {
                    printf("最小生成树不唯一\n");
                    return;
                }
            }
        }
        Union(a, b);
        T--;
        ans += w;
        //printf("%d--%d:%d  ", a, b, w);
    }printf("\n\n");
    printf("最小生成树的权值为:%d\n", ans);
}

完整的代码如下:

#define maxsize 100

//并查集
int father[maxsize] = { 0 };
int Find(int x)
{
    if (father[x] <= 0) return x;
    return father[x] = Find(father[x]);
}
void Union(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy) return;
    if (father[fx] < father[fy])
    {
        father[fy] = fx;
    }
    else
    {
        if (father[fx] == father[fy])
        {
            father[fy]--;
        }
        father[fx] = fy;
    }
}
//对边进行排序,并存入edge数组中
int sort_edge(int graph[][6], int n, int edge[][3])
{
    int edgenum = 0;  //k为边的数量
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            if (graph[i][j])
            {
                edgenum++;
                edge[edgenum][0] = i;
                edge[edgenum][1] = j;
                edge[edgenum][2] = graph[i][j];
            }
        }
    }
    for (int i = edgenum; i > 1; i--)
    {
        int flag = 0;
        for (int j = 1; j < i; j++)
        {
            if (edge[j][2] > edge[j + 1][2])
            {
                flag = 1;

                int temp = edge[j][0];
                edge[j][0] = edge[j + 1][0];
                edge[j + 1][0] = temp;

                temp = edge[j][1];
                edge[j][1] = edge[j + 1][1];
                edge[j + 1][1] = temp;

                temp = edge[j][2];
                edge[j][2] = edge[j + 1][2];
                edge[j + 1][2] = temp;
            }
        }
        if (!flag) break;
    }
    printf("排序后的边为:");
    for (int i = 1; i <= edgenum; i++)
    {
        printf("%d--%d:%d  ", edge[i][0], edge[i][1], edge[i][2]);
    }printf("\n\n");
    return edgenum;
}
//kruskal算法
void kruskal(int graph[][6], int n)
{
    int edge[maxsize][3];
    int edgenum = sort_edge(graph, n, edge);
    int T = n;    //集合的个数
    int k = 1;
    int ans = 0;
    //printf("最小生成树:");
    while (T > 1)
    {
        int a = edge[k][0];
        int b = edge[k][1];
        int w = edge[k][2];
        k++;

        if (Find(a) == Find(b)) continue;
        //由于边是降序排序的,因此可以往后查找与当前的边权值相同的边
        for (int i = k; i <= edgenum; i++)
        {
            if (edge[i][2] == w)    //如果 a--b 和 newa--newb 这两边权值相同
            {
                int newa = edge[i][0];
                int newb = edge[i][1];
                //并且两边所处的两个连通块都一样,就说明最小生成树不唯一
                if (Find(a) == Find(newa) && Find(b) == Find(newb) || Find(a) == Find(newb) && Find(b) == Find(newa))
                {
                    printf("边 %d--%d 与边 %d-- %d 可任意选,最小生成树不唯一\n", a, b, newa, newb);
                    return;
                }
            }
        }
        Union(a, b);
        T--;
        ans += w;
        //printf("%d--%d:%d  ", a, b, w);
    }printf("\n\n");
    printf("最小生成树的权值为:%d\n", ans);
}

int main()
{
    int graph[6][6] = { 0 };
    int n = 5;
    graph[1][4] = 1;    graph[4][1] = 1;
    graph[1][3] = 2;    graph[3][1] = 2;
    graph[1][2] = 3;    graph[2][1] = 3;
    graph[3][4] = 4;    graph[4][3] = 4;
    graph[4][5] = 5;    graph[5][4] = 5;
    graph[2][5] = 6;    graph[5][2] = 6;
    graph[2][4] = 3;    graph[4][2] = 3;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%d ", graph[i][j]);
        }printf("\n");
    }printf("\n");

    kruskal(graph, n);
    return 0;
}

对于下图来说:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

 上述代码的打印结果为:

相关文章:

  • Android中常用的几种容器视图的使用
  • 随手记面试录
  • VMware软件下载安装以及在VMware中安装Centos-stream
  • JCL入门教程
  • 5.6如何寻找最长回文子串
  • tkinter-event事件
  • Windows10环境gradle安装与配置
  • DELMIA弧焊虚拟仿真:带变位机的机器人弧焊焊接程序自动生成方法
  • Redis 非关系型数据库学习(三)---- Redis 基础知识
  • 离线数仓(2):数据仓库相关架构和规范
  • MySQL-数据类型和DDL
  • Linux学习笔记6 - 系统启动流程
  • 动态数组写模板类
  • 代码错误与检查简短教程(新手适用)
  • Java Design Patterns 之API 网关模式
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 《深入 React 技术栈》
  • Angular 2 DI - IoC DI - 1
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • axios 和 cookie 的那些事
  • C++入门教程(10):for 语句
  • css选择器
  • emacs初体验
  • java多线程
  • js 实现textarea输入字数提示
  • Laravel5.4 Queues队列学习
  • Mybatis初体验
  • React Transition Group -- Transition 组件
  • 阿里研究院入选中国企业智库系统影响力榜
  • 前端js -- this指向总结。
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 三分钟教你同步 Visual Studio Code 设置
  • 提醒我喝水chrome插件开发指南
  • 微信支付JSAPI,实测!终极方案
  • 译自由幺半群
  • 用jQuery怎么做到前后端分离
  • Hibernate主键生成策略及选择
  • 湖北分布式智能数据采集方法有哪些?
  • 如何在招聘中考核.NET架构师
  • 组复制官方翻译九、Group Replication Technical Details
  • ​一些不规范的GTID使用场景
  • # 学号 2017-2018-20172309 《程序设计与数据结构》实验三报告
  • (3)llvm ir转换过程
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (转)setTimeout 和 setInterval 的区别
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .Net MVC + EF搭建学生管理系统
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • ??在JSP中,java和JavaScript如何交互?
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • @Mapper作用
  • @RequestMapping用法详解