首页 > 编程语言 > 详细

简述最小生成树的算法

时间:2019-11-18 19:18:43      阅读:94      评论:0      收藏:0      [点我收藏+]

最小生成树


接下来是介绍最小生成树的内容。

什么是最小生成树?

定义: 各边的代价之和最小的那棵生成树称为该连通网的最小代价生成树( Minimum Cost Spanning Tree ),简称最小生成树。

构成最小生成树的算法有很多,其中多数的算法都是利用了最下生成树的一种性质:

性质: 假设N=(V,E)是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(或者说代价)的边,其中u∈U,v∈V-U,那么必定存在一颗包含边(u,v)的最小生成树

总结起来,得具备这些条件:

  • 是一个连通网(连通图)
  • 各边有权值

如果还不了解,那么我们来举一个例子:

比方说我们需要在n个城市中建立一个联系网,因为城市众多,我们当然不可能一一给他们都连通,考虑到价格问题,我们必须得用最小的代价从而连通所有的城市。

该怎么做呢?好难(っ °Д °;)っ


我们用圆圈来表示每个城市

技术分享图片

然后用箭头中包含的值来表示权值

技术分享图片

我们的问题来了,我们该怎样去选择,从而达到我们的目的呢? ( 我们的目的是:德玛西亚d=====( ̄▽ ̄*)b.......当然....目的是选择几条路线,组成的权值最小,并且能够把这几个点连接上...... )

首先我们用眼睛观察.....然后我们很容易选出这几条路线:

技术分享图片

然后我们来算算,总权值是:1+2+4+5+12=24。没错,这就是最小生成树的构成,我们所选的路线并没有错。那么问题来了。我们是 怎么选 的呢?

还记得我们刚刚选的方式么?我的思路是,先把他们的各个边按照权值从小到大排序,排完序之后,再从中选出我所需要的边,直到所有的点覆盖完成,当然,如果有一条边是存在于已经连过的两个顶点之间,我就不要了,因为这两个城市已经走过了(●ˇ?ˇ●)。

当所有的顶点都连接完成之后,我们的最小生成树也就构成了。如果排完序那么我们是这样的一个序列:

城市 可通城市 权值
A B 1
A D 2
B F 4
A E 5
E F 9
A C 12
B C 13
C D 18
D E 32

然后对应 A B C D E F 转换为数字便是: 1 2 3 4 5 6

然后,我们从小到大,选择便是以下的情况:
技术分享图片
技术分享图片
技术分享图片
技术分享图片
技术分享图片

由于我们有六个点,那么我们选择五条边,就完成了我们所需要的最小生成树。没错,这便是我要说的最小生成树的一种算法:(Kruskal 克鲁斯卡尔算法)

比较官方的构造过程便是:

  • 假设连通网N=(V,E),将N中的边按权值从小到大的顺序排列
  • 初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。
  • 在E中选择权值最小的边,若该边依附的顶点落在T中不同的连通分量上(即不形成回路),则将此边加入到T中,否则舍去此边而选择下一条权值最小的边
  • 重复操作,直到T中所有顶点都在同一连通分量上为止

一波官方的解释,看着我有点懵 (@_@) ,但是没事,我有自己的理解。接下来便是代码实现了......


main.cpp

#include"GraphKruskal.h"

int main()
{
    GraphKurskal g1;
    g1.Init();
    g1.Kruskal();
    g1.Display();
    return 0;
}

/*
输入样例:
1 1 1
1 4 2
2 6 4
1 5 5
5 6 9
1 3 12
2 3 13
3 4 18
4 5 32

*/

GraphKruskal.h

#pragma once
#ifndef _GRAPHKRUSKAL_H_
#define _GRAPHKRUSKAL_H_
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
//  这是利用邻接矩阵来实现的克鲁斯卡尔搜索最小生成树的算法
struct node
{
    int from, to;
    int sideID;//记录边号,已选定可以输出的边
    int distance;
};

class GraphKurskal
{
private:
    vector<node>graph;//记录边
    vector<bool>visited;//是否经历过
    vector<int>IDofSide;//每条边的ID
    int minDistacne;//最小权值
    int nodeNumber;//几个点

    //void Sort();//这个程序是调用库函数的sort函数进行排序,因此没有写自定义sort函数
public:
    void Init();//初始化
    void Kruskal();//进行克鲁斯卡尔算法,找到最小生成树
    void Display();//显示

};
bool cmp(node a1,node a2);//从小到大排序的函数

#endif // !_GRAPHKRUSKAL_H_

GraphKruskal.cpp

#include "GraphKruskal.h"


void GraphKurskal::Init()
{
    node n1;
    int i = 0;
    cout << "请输入有几个点" << endl;
    cin >> nodeNumber;
    visited.resize(nodeNumber + 1, false);
    cout << "请输入各边的关系,'#'结束" << endl;
    cout << "请按照格式:开始点---终止点---距离" << endl;
    while ((cin >> n1.from&& n1.from != '#') && (cin >> n1.to&& n1.to != '#'))
    {
        cin >> n1.distance;
        n1.sideID = ++i;
        graph.push_back(n1);
    }
}

void GraphKurskal::Kruskal()
{

    sort(graph.begin(), graph.end(),cmp);//进行函数比较
        //当选择的边数不为节点数减一的情况,我们继续循环
    minDistacne = 0;
    for (int i = 0; i < graph.size(); i++)
    {
        //判断图中的点是否遍历过,如果遍历过,那么跳过,否则继续遍历
        if (!visited[graph[i].from] || !visited[graph[i].to])
        {
            visited[graph[i].from] = true;
            visited[graph[i].to] = true;
            IDofSide.push_back(graph[i].sideID);
            minDistacne += graph[i].distance;
            if (IDofSide.size() == nodeNumber - 1)
                break;
        }
    }
}

void GraphKurskal::Display()
{
    cout << "总距离是:" << minDistacne << endl<<"权边是:";
    for (int i = 0; i < IDofSide.size(); i++)
        cout << graph[IDofSide[i]-1].distance << " ";
    cout << endl;
}

bool cmp(node a1, node a2)
{
    return a1.distance > a2.distance ? false : true;
}

代码实现结果:
技术分享图片


借鉴《数据结构C语言版|第2版》


简述最小生成树的算法

原文:https://www.cnblogs.com/Yunrui-blogs/p/11884247.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!