最小生成树(Kruskal克鲁斯卡尔算法)
最小生成树(克鲁斯卡尔算法)
如果想看普利姆算法,请移步
-------->Prime普利姆算法<--------
接下来是介绍最小生成树的内容。
什么是最小生成树?
定义: 各边的代价之和最小的那棵生成树称为该连通网的最小代价生成树( 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版》
这是小睿的博客,如果需要转载,请标注出处啦~ヾ(≧▽≦*)o谢谢。