最小生成树

    最小生成树MST是在一个图中求出一个连接N个点的树,使得树的N-1条边的权值之和最小。 
    求最小生成树有两种方法:1. prim算法 2.kruskal算法

prim算法

    贪心思想,将N个点分为两个集合。V(在最小生成树中的点集合)和S-V(不在最小生成树中的点集合),每次从S-V集合中选取 距离集合V中的点的距离最小的点,加入V集合。直到集合S-V为空。 
实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
struct Edge{
    int vertex;
    int dist;
    Edge(int v, int d) :
        vertex(v), dist(d){};
};
vector<vector<Edge> > gGraph;
struct Compare{
    bool operator()(const Edge& e1, const Edge& e2){
        return e1.dist > e2.dist;
    }
};
 
bool gVisited[MAX_NODE];
 
int Prim(int n){    //prim算法加堆优化
    memset(gVisited, false, sizeof(gVisited));
    //随便选择一个起点,假设从0开始
    Edge e(0, 0);
    int sum_mst = 0;
    priority_queue<Edge, vector<Edge>, Compare> pq;
    pq.push(e);
    while (!pq.empty()){
        e = pq.top();
        pq.pop();
        if (gVisited[e.vertex])
            continue;
        gVisited[e.vertex] = true;
        sum_mst += e.dist;
        for (int i = 0; i < gGraph[e.vertex].size(); i++){   //将该点连接边都加入到优先队列中,进行下一轮选择
            pq.push(gGraph[e.vertex][i]);
        }
    }
    return sum_mst;
}

 

kruskal算法

    贪心思想。将所有的边进行排序,然后按照边长从小到大的顺序,判断边连接的两点是否已经同时在最小生成树上了,若在,则抛弃该边(为了避免形成环),继续;否则将该边加入最小生成树。 
实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//边的数据结构
struct Edge{
    int from;
    int to;
    double dist;
    Edge(int f, int t, double d) :
        from(f), to(t), dist(d){};
};
vector<Edge> gEdges;
 
//用并查集来判断加入一条边是否会构成环
int gRoot[MAX_NODE];
int GetRoot(int c){
    if (gRoot[c] != c){
        gRoot[c] = GetRoot(gRoot[c]);
    }
    return gRoot[c];
}
bool SameRoot(int c1, int c2){
    int p1 = GetRoot(c1);
    int p2 = GetRoot(c2);
    return p1 == p2;
}
 
void Union(int c1, int c2){
    int p1 = GetRoot(c1);
    int p2 = GetRoot(c2);
    gRoot[p1] = p2;
}
//用于对边进行排序
bool Compare(const Edge& e1, const Edge& e2){
    return e1.dist < e2.dist;
}
 
 
double Kruskal(int s, int n){
    double result;
    for (int i = 0; i < n; i++){
        gRoot[i] = i;
    }
    sort(gEdges.begin(), gEdges.end(), Compare); //无向图的边只存储了 从序号较小的节点指向序号较大的节点
    int count = 0;
    for (int i = 0; i < gEdges.size(); i++){
        Edge& e = gEdges[i];
        if (SameRoot(e.from, e.to))
            continue;
 
        count++;
        if (count == n - s){
            //从最小生成树中的n-1条边,去掉最大的s-1条边(因为有s个卫星站,相当于s个点,则s-1条边)
            //,剩下的n-1-s条边中,最大的边长即为所求
            result = e.dist;
            return result;
        }
         
        Union(e.to, e.from);
        //gRoot[gRoot[e.to]] = gRoot[e.from]; //注意合并的时候,将 to 的根更新为 from的根。因为所有的边只存储了从小序号指向大序号
    }
    return 0;
}

 

posted @   农民伯伯-Coding  阅读(267)  评论(0)    收藏  举报
编辑推荐:
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
阅读排行:
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 管理100个小程序-很难吗
· 基于Blazor实现的运输信息管理系统
· 如何统计不同电话号码的个数?—位图法
· 聊聊四种实时通信技术:长轮询、短轮询、WebSocket 和 SSE
点击右上角即可分享
微信分享提示