Fork me on GitHub

Kruskal算法

Kruskal算法是一种用于求解最小生成树(Minimum Spanning Tree, MST)的贪心算法。最小生成树是指在一个带权无向图中,选取一棵包含所有顶点的树,使得树上所有边的权值之和最小。

 算法步骤
1. 排序:将所有边按权值从小到大排序。
2. 初始化:创建一个空的森林(即每个顶点单独构成一棵树)。
3. 选择边:按顺序选择权值最小的边,如果这条边连接的两棵树不在同一棵树中(即不会形成环),则将其加入最小生成树中,并合并这两棵树。
4. 重复:重复步骤3,直到最小生成树中有 (V-1) 条边 (V) 是顶点数)。

 伪代码

Kruskal(G):
1. 对图G的所有边按权值升序排序
2. 初始化一个空的森林F
3. 对于每条边(u, v) in G的边(按排序后的顺序):
a. 如果u和v不在同一棵树中:
i. 将边(u, v)加入F
ii. 合并u和v所在的树
4. 返回F

  

实现细节
- 并查集(Disjoint Set Union, DSU):用于高效地判断两个顶点是否在同一棵树中,以及合并两棵树。
- 时间复杂度:排序边的时间复杂度为 O(E log E)),并查集操作的时间复杂度为 O(E log V),因此总时间复杂度为 O(E log E) 或 O(E log V)(因为 E <= V^2)。

应用场景

Kruskal算法常用于网络设计、电路设计、聚类分析等领域,适用于稀疏图(边数较少的情况)。

#include <stdio.h>
#include <stdlib.h>

// 定义边的结构体
struct Edge {
    int src, dest, weight;
};

// 定义图的结构体
struct Graph {
    int V, E;           // V是顶点数,E是边数
    struct Edge* edge;   // 边的数组
};

// 创建图
struct Graph* createGraph(int V, int E) {
    struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
    graph->V = V;
    graph->E = E;
    graph->edge = (struct Edge*)malloc(E * sizeof(struct Edge));
    return graph;
}

// 并查集的查找函数(带路径压缩)
int find(int parent[], int i) {
    if (parent[i] == i)
        return i;
    return parent[i] = find(parent, parent[i]); // 路径压缩
}

// 并查集的合并函数(按秩合并)用于保证合并时的平衡性,避免树变得过高,从而提升 find 操作的效率。
void Union(int parent[], int rank[], int xroot, int yroot) {
    if (rank[xroot] < rank[yroot])
        parent[xroot] = yroot;
    else if (rank[xroot] > rank[yroot])
        parent[yroot] = xroot;
    else {
        parent[yroot] = xroot;
        rank[xroot]++;
    } 
}

// 比较函数,用于边的排序
int compare(const void* a, const void* b) {
    struct Edge* a1 = (struct Edge*)a;
    struct Edge* b1 = (struct Edge*)b;
    return a1->weight - b1->weight;
}

// Kruskal算法实现
void KruskalMST(struct Graph* graph) {
    int V = graph->V;
    struct Edge result[V]; // 存储最小生成树的边
    int e = 0;             // 用于result数组的索引
    int i = 0;             // 用于排序后的边数组的索引

    // 1. 对所有边按权值排序
    qsort(graph->edge, graph->E, sizeof(graph->edge[0]), compare);

    // 初始化并查集
    int* parent = (int*)malloc(V * sizeof(int));
    int* rank = (int*)malloc(V * sizeof(int));

    for (int v = 0; v < V; v++) {
        parent[v] = v;
        rank[v] = 0;
    }

    // 2. 选择边,直到最小生成树有 V-1 条边
    while (e < V - 1 && i < graph->E) {
        // 选择权值最小的边
        struct Edge next_edge = graph->edge[i++];

        int xroot = find(parent, next_edge.src);
        int yroot = find(parent, next_edge.dest);

        // 如果不会形成闭环,则加入结果
        if (xroot != yroot) {
            result[e++] = next_edge;
            Union(parent, rank, xroot, yroot);
        }
    }

    // 输出最小生成树的边
    printf("最小生成树的边:\n");
    for (i = 0; i < e; i++)
        printf("%d -- %d 权值: %d\n", result[i].src, result[i].dest, result[i].weight);

    // 释放内存
    free(parent);
    free(rank);
}

int main() {
    int V = 4; // 顶点数
    int E = 5; // 边数
    struct Graph* graph = createGraph(V, E);

    // 添加边
    graph->edge[0].src = 0;
    graph->edge[0].dest = 1;
    graph->edge[0].weight = 10;

    graph->edge[1].src = 0;
    graph->edge[1].dest = 2;
    graph->edge[1].weight = 6;

    graph->edge[2].src = 0;
    graph->edge[2].dest = 3;
    graph->edge[2].weight = 5;

    graph->edge[3].src = 1;
    graph->edge[3].dest = 3;
    graph->edge[3].weight = 15;

    graph->edge[4].src = 2;
    graph->edge[4].dest = 3;
    graph->edge[4].weight = 4;

    // 运行Kruskal算法
    KruskalMST(graph);

    // 释放图的内存
    free(graph->edge);
    free(graph);

    return 0;
}

 

posted @ 2025-03-14 14:40  z_s_s  阅读(398)  评论(0)    收藏  举报