【图论】最小生成树与Prim、Kruskal算法

本文介绍了Prim和Kruscal算法,基于最小生成树的性质,通过松弛操作构建最小生成树,以及如何在给定数据结构中实现这两个算法。实例代码展示了如何使用C++进行最小范围计算的过程。
摘要由CSDN通过智能技术生成

求图的最小生成树的Prim、Kruscal算法,其实都是由最小生成树的性质推来的,掌握了该性质,便能较容易地推导出这两种算法。

最小生成树的性质

无向图G的顶点集为 V V V,设 U U U V V V的真子集, u ∈ U u\in U uU v ∈ ( V − U ) v\in (V- U) v(VU),边u-v是连通 U U U、的所有边中权重最小的一条,则边u-v一定在G的最小生成树(MST)中。
反证法证明:假设T是一个MST,边u-v不在该MST中,先把它加入到T,则出现包含该边的环;T中必存在另一条边 u ′ − v ′ u^\prime-v^\prime uv,连接题中两个集合,删除它后,环消失,出现新的MST,并且比原MST的权重更小,矛盾。

Prim算法

思路就是任取一个顶点作为初始U,不断寻找连接两个子集的所有边中权重最小的一条,把此边在 V − U V-U VU上的顶点移到 U U U,重复这个操作,直至U包含全部顶点。

Kruscal算法

直接将所有边按权重排序,从小到大,每次尝试添加一条到MST,如果加入后没有产生环就加入该边,否则跳过。

松弛操作

两种算法都用了求最值问题的松弛操作思路,相似的例子,求K个已排序数组的最短公共区间,该区间包含每个数组至少一个元素,初始化选择各个数组首个元素组成集合的最小最大值,然后松弛,选择最小值所在数组向右取元素,更新区间。

#include <vector>
#include <utility>
#include <numeric>
#include <iostream>
#include <limits>
#include <tuple>
using namespace std;
class Solution {
public:
    vector<int> smallestRange(vector<vector<int> > &nums)
    {
        bool stop = false;
        int length = 2e5;
        int left = -1e5;
        int right = 1e5;
        vector<size_t> v_pos;
        vector<size_t> v_size;
        vector<int> holder;
        std::vector<int> lrlen{left, right, length};
        Init(nums, v_pos, v_size, holder);
        UpdateLeftRight(nums, v_pos, v_size, holder, lrlen, stop);
        while (!stop) {
            UpdateLeftRight(nums, v_pos, v_size, holder, lrlen, stop);
        }
        return {lrlen[0], lrlen[1]};
    }

    tuple<size_t, int, int> GetMinMax(const vector<int> &holder_)
    {
        int min = numeric_limits<int>::max();
        int max = numeric_limits<int>::min();
        size_t pos = 0;
        for (size_t i = 0; i < holder_.size(); i++) {
            if (min > holder_[i]) {
                min = holder_[i];
                pos = i;
            }
            if (max < holder_[i]) {
                max = holder_[i];
            }
        }
        return {pos, min, max};
    }
    void UpdateLeftRight(const vector<vector<int> > &nums, vector<size_t> &v_pos, vector<size_t> &v_size,
                         vector<int> &holder, std::vector<int> &lrlen, bool &stop)
    {
        auto res = GetMinMax(holder);
        int tmp_left = get<1>(res);
        int tmp_right = get<2>(res);
        size_t pos = get<0>(res);
        if (v_pos[pos] == (nums[pos].size() - 1)) {
            stop = true;
        }
        v_pos[pos]++;
        int tmp_length = tmp_right - tmp_left;
        if (tmp_length < lrlen[2] || (tmp_length == lrlen[2] && tmp_left < lrlen[0])) {
            lrlen[0] = tmp_left;
            lrlen[1] = tmp_right;
            lrlen[2] = tmp_length;
        }
        holder.clear();
        for (size_t i = 0; i < v_pos.size(); i++) {
            holder.push_back(nums[i][v_pos[i]]);
        }
    }
    void Init(const vector<vector<int> > &nums, vector<size_t> &v_pos, vector<size_t> &v_size, vector<int> &holder)
    {
        v_pos.clear();
        v_size.clear();
        holder.clear();
        for (size_t i = 0; i < nums.size(); i++) {
            v_pos.push_back(0);
            v_size.push_back(nums[i].size());
            holder.push_back(nums[i][0]);
        }
    }
};
int main()
{
    Solution A;
    vector<int> a{4, 10, 15, 24, 26};
    vector<int> b{0, 9, 12, 20};
    vector<int> c{5, 18, 22, 30};
    vector<vector<int> > q{a, b, c};
    auto res = A.smallestRange(q);
    for (auto i : res) {
        cout << i << " ";
    }
    cout << endl;
    a.clear();
    b.clear();
    c.clear();
    for (int i = 1; i < 4; i++) {
        a.push_back(i);
        b.push_back(i);
        c.push_back(i);
    }
    vector<vector<int> > qb{a, b, c};
    res = A.smallestRange(qb);
    for (auto i : res) {
        cout << i << " ";
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值