首页 > 其他分享 >图(二)最小生成树

图(二)最小生成树

时间:2024-11-18 20:15:08浏览次数:3  
标签:minTree srci 最小 生成 算法 ._ AddEdge size

1.概念

连通图中的每一棵生成树,都是原图的一个极大无环子图,

即:从其中删去任何一条边,生成树就不在连通;

反之,在其中引入任何一条新边,都会形成一条回路。

2.步骤

若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边。

因此构造最小生成树的准则有三条:

  1. 只能使用图中的边来构造最小生成树

  2. 只能使用恰好n-1条边来连接图中的n个顶点

  3. 选用的n-1条边不能构成回路

3.方法

构造最小生成树的方法:Kruskal算法和Prim算法。

这两个算法都采用了逐步求解的贪心策略。

贪心算法:是指在问题求解时,总是做出当前看起来最好的选择。

也就是说贪心算法做出的不是整体最优的的选择,而是某种意义上的局部最优解。

贪心算法不是对所有的问题都能得到整体最优解。

1.Kruskal算法

任给一个有n个顶点的连通网络N={V,E},

首先构造一个由这n个顶点组成、不含任何边的图G={V,NULL},其中每个顶点自成一个连通分量,

其次不断从E中取出权值最小的一条边(若有多条任取其一),

若该边的两个顶点来自不同的连通分量,则将此边加入到G中。

如此重复,直到所有顶点在同一个连通分量上为止。

核心:每次迭代时,选出一条具有最小权值,且两端点不在同一连通分量上的边,加入生成树。

image-20241117222446609

实现

W Kruskal(Self& minTree)
{
    int n = _vertexs.size();
​
    minTree._vertexs = _vertexs;
    minTree._indexMap = _indexMap;
    minTree._matrix.resize(n);
​
    for (size_t i = 0; i < n; ++i)
    {
        minTree._matrix[i].resize(n, MAX_W);
    }
​
    priority_queue<Edge, vector<Edge>, greater<Edge>> minque;
    for (size_t i = 0; i < n; i++)
    {
        for (size_t j = 0; j < n; j++)
        {
            if (i < j && _matrix[i][j] != MAX_W)
            {
                minque.push(Edge(i, j, _matrix[i][j]));
            }
        }
    }
​
    int size = 0;
    W totalW = W();
    UnionFindSet ufs(n);
    while (!minque.empty())
    {
        Edge min = minque.top();
        minque.pop();
​
        if (!ufs.InSet(min._srci, min._dsti))
        {
            cout << _vertexs[min._srci] << "-" << _vertexs[min._dsti] << "->" << min._w << endl;
            minTree._AddEdge(min._dsti, min._srci, min._w);
            ufs.Union(min._srci, min._dsti);
            ++size;
            totalW += min._w;
        }
        else
        {
            cout << "构成环:";
            cout << _vertexs[min._srci] << "-" << _vertexs[min._dsti] << "->" << min._w << endl;
        }
    }
    cout << endl;
    if (size == n - 1)
    {
        return totalW;
    }
    else
    {
        return W();
    }
}
​
​
void TestKruskal()
{
    const char* str = "abcdefghi";
    Graph<char, int> g(str, strlen(str));
    g.AddEdge('a', 'b', 4);
    g.AddEdge('a', 'h', 8);
    //g.AddEdge('a', 'h', 9);
    g.AddEdge('b', 'c', 8);
    g.AddEdge('b', 'h', 11);
    g.AddEdge('c', 'i', 2);
    g.AddEdge('c', 'f', 4);
    g.AddEdge('c', 'd', 7);
    g.AddEdge('d', 'f', 14);
    g.AddEdge('d', 'e', 9);
    g.AddEdge('e', 'f', 10);
    g.AddEdge('f', 'g', 2);
    g.AddEdge('g', 'h', 1);
    g.AddEdge('g', 'i', 6);
    g.AddEdge('h', 'i', 7);
​
    Graph<char, int> kminTree;
    cout << "Kruskal:" << g.Kruskal(kminTree) << endl;
    kminTree.Print();
}

输出结果:

image-20241117224638750

image-20241117224701339

2.Prim算法

Prim 算法的工作原理与 Dijkstra的最短路径算法相似。

Prim算法所具有的一个性质是集合A中的边总是构成一棵树。

一棵树从一个任意的根结点开始,一直长大到覆盖V中的所有结点时为止。

算法每一步在连接集合A和A之外的结点的所有边中,选择一条轻量级边加入到A中。

这条规则所加入的边都是对A安全的边。

因此,当算法终止时,A中的边形成一棵最小生成树。

本策略也属于贪心策略,因为每步所加入的边都必须是使树的总权重增加量最小的边。

实现:

W Prim(Self& minTree, const W& src)
{
    size_t srci = GetVertexIndex(src);
    int n = _vertexs.size();
​
    minTree._vertexs = _vertexs;
    minTree._indexMap = _indexMap;
    minTree._matrix.resize(n);
​
    for (size_t i = 0; i < n; ++i)
    {
        minTree._matrix[i].resize(n, MAX_W);
    }
​
    /*set<int> X;
    set<int> Y;
    X.insert(srci);
    for (size_t i = 0;i < n; i++)
    {
        if (i != srci)
        {
            Y.insert(i);
        }
    }
    */
​
    vector<bool> X(n, false);
    vector<bool> Y(n, true);
    X[srci] = true;
    Y[srci] = false;
    
​
    //从X->Y集合连接边里面选出最小的边
    priority_queue<Edge, vector<Edge>, greater<Edge>> minque;
    //先把srci连接的边添加到队列中
    for (size_t i = 0; i < n; i++)
    {
        if (_matrix[srci][i] != MAX_W)
        {
            minque.push(Edge(srci, i, _matrix[srci][i]));
        }
    }
​
    cout << "Prim开始选边" << endl;
​
    size_t size = 0;
    W totalW = W();
    while (!minque.empty())
    {
        Edge min = minque.top();
        minque.pop();
​
        //判环!
        //最小边的目标点也在X集合,则构成环
        if (X[min._dsti])
        {
            cout << "构成环的边:";
            cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << "->" << min._w << endl;
​
        }
        else
        {
            minTree._AddEdge(min._srci, min._dsti, min._w);
            cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << "->" << min._w << endl;
            X[min._dsti] = true;
            Y[min._dsti] = false;
            ++size;
            totalW += min._w;
​
            if (size == n - 1)
            {
                break;
            }
​
            for (size_t i = 0; i < n; i++)
            {
                if (_matrix[min._dsti][i] != MAX_W && Y[i])
                {
                    minque.push(Edge(min._dsti, i, _matrix[min._dsti][i]));
                }
            }
        }
    }
    if (size == n - 1)
    {
        return totalW;
    }
    else
    {
        return W();
    }
}
​
void TestPrim()
{
    const char* str = "abcdefghi";
    Graph<char, int> g(str, strlen(str));
    g.AddEdge('a', 'b', 4);
    g.AddEdge('a', 'h', 8);
    //g.AddEdge('a', 'h', 9);
    g.AddEdge('b', 'c', 8);
    g.AddEdge('b', 'h', 11);
    g.AddEdge('c', 'i', 2);
    g.AddEdge('c', 'f', 4);
    g.AddEdge('c', 'd', 7);
    g.AddEdge('d', 'f', 14);
    g.AddEdge('d', 'e', 9);
    g.AddEdge('e', 'f', 10);
    g.AddEdge('f', 'g', 2);
    g.AddEdge('g', 'h', 1);
    g.AddEdge('g', 'i', 6);
    g.AddEdge('h', 'i', 7);
​
    Graph<char, int> pminTree;
    cout << "Prim:" << g.Prim(pminTree, 'a') << endl;
    pminTree.Print();
    cout << endl;
​
    for (size_t i = 0; i < strlen(str); i++)
    {
        cout << "Prim:" << g.Prim(pminTree, str[i]) << endl;
​
        cout << endl;
    }
}

输出结果:

image-20241117225032311

image-20241117225056283

image-20241117225123311

标签:minTree,srci,最小,生成,算法,._,AddEdge,size
From: https://blog.csdn.net/lll_666666/article/details/143865466

相关文章

  • 随机生成LDPC码并类下三角简化编码
    %function[H]=genH(rows,cols)%************设置参数***************rows=8;cols=24;u=[11010001110011011];%**********创建一个1*12的全为零的数组row_flag*******%**********创建一个24*12的全为零的矩阵parity_check**row_flag(1:rows)=0;parity_......
  • 生成器-yield
    yiled返回一个迭代对象,作用在函数里,其作用类似于returnyield是Python中的一个关键字,用于定义生成器。生成器是一种特殊的迭代器,它可以逐步生成值,而不是一次性返回所有值。使用yield可以提高程序的效率,特别是在处理大量数据时,因为它允许你在需要的时候生成数据,而不是一次......
  • AIGC----生成对抗网络(GAN)如何推动AIGC的发展
    AIGC:生成对抗网络(GAN)如何推动AIGC的发展前言随着人工智能领域的迅猛发展,AI生成内容(AIGC,AIGeneratedContent)正成为创意产业和技术领域的重要组成部分。在AIGC的核心技术中,生成对抗网络(GAN,GenerativeAdversarialNetwork)被认为是推动AIGC发展的关键力量之一。本篇博......
  • 使用 PyTorch 从头构建最小的 LLM 该项目构建了一个简单的字符级模型
    简介我开始尝试各种受Pokémon启发的猫名变体,试图赋予它独特、略带神秘感的氛围。在尝试了“Flarefluff”和“Nimblepawchu”等名字后,我突然想到:为什么不完全使用人工智能,让字符级语言模型来处理这个问题呢?这似乎是一个完美的小项目,还有什么比创建自定义Pokémon名......
  • 多模态大模型LLM与AIGC前沿技术实战,基于训练数据和生成算法模型
    多模态大模型LLM与AIGC前沿技术实战,基于训练数据和生成算法模型在当今人工智能领域,多模态大模型LLM(大型语言模型)与AIGC(人工智能生成内容)正以前所未有的发展态势,引领着技术革新的浪潮。它们的强大能力背后,训练数据和生成算法模型起着至关重要的作用,深入探究这两方面并了解其在实......
  • PHP生成微信小程序太阳码
    先获取微信的接口调用凭证functionaccessToken($appId,$appSecret){$tokenFile='access_token.json';//保存的access_token$data=json_decode(file_get_contents($tokenFile));if($data->expire_time<time()){//调用微信接口获取access_......
  • 代码随想录算法训练营第三十二天| 509. 斐波那契数 、70. 爬楼梯、746. 使用最小花费
    理论基础总结一下就是:动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。动态规划五部曲确定dp数组(dptable)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组509.斐波那契数1.......
  • leetcode1963. 使字符串平衡的最小交换次数
    给你一个字符串 s ,下标从0开始 ,且长度为偶数 n 。字符串 恰好 由 n/2 个开括号 '[' 和 n/2 个闭括号 ']' 组成。只有能满足下述所有条件的字符串才能称为 平衡字符串 :字符串是一个空字符串,或者字符串可以记作 AB ,其中 A 和 B 都是 平衡字符串......
  • 小寄巧——给洛谷题单快速生成一份目录
    以此题单为例,首先我们在浏览器中打开,F12切换到Console,输入document.querySelectorAll(".titlea"),然后复制返回的所有内容,粘贴到VSCode里,内容大致如下:NodeList(15)[a.title.color-default,a.title.color-default,a.title.color-default,a.title.color-default,a.title......
  • 生成 Windows 窗体 Blazor 应用 (WinForm+Bootstrap Blazor)
    官方文档有介绍如何用WinForm+ Blazor  生成应用,  生成Windows窗体Blazor应用 先按照官方文档启动VisualStudio。在“开始”窗口中,选择“创建新项目”:创建WinForm项目  起名为:WinFormsBlazor框架我们选择:.NET8.0 创建完成项目后,使用NuGet包管理器......