Kruskal算法(并查集+QuickSort)
概述
kruskal算法主要是根据贪心算法和并查集算法结合实现的。
贪心策略
在所有边中选择权值最小的边。
条件
所选择的边所在的点不能是树中的结点(不能形成回路,根据并查集实现)
思路
每次选择最小的边,直到所有的点连成树。
步骤
整个代码由三大部分组成:
1、初始化
2、排序
3、选择加入
Kruskal算法与 Prim算法的区别:
贪心策略不同,Kruskal算法是在所有边中选择最小的边,
Prim算法是在与已经连接的结点相邻的结点中选择最小的边。
Prim算法适合稠密图,Kruskal算法适合稀疏图
Kruskal算法与 并查集算法的区别:
Kruskal算法形成的是最小生成树,而并查集只是判断结点是否在同一颗树,并且将其连成树。
代码中关键部分
1、如何存储边的关系。我采用的是结构体的方式,存放每组边的权值以及两个结点。
2、如果对各组边进行从小到大的排序。采用的是在快速排序的基础上对每组边进行交换。
3、如何与并查集结合使用。最开始我以为对边进行排序会影响到并查集的使用,但是后来我发现两者没有关系,并查集只需要对已排好序的边进行判断将要相连的结点是否在同一个树中。
代码用例
#include <iostream>
#include <algorithm>
using namespace std;
const int inf = 88888;
int n,m;
int a,b,c;
int map[100][100]; //图结构
int dis[100];
int id[100]; //并查集
//边关系
typedef struct
{
int weight; //权值
int begin; //开始结点
int end; //结束结点
}Element1;
//初始化
void init(Element1 Element[])
{
//初始化图
for(int i = 0; i < n; i++)
for(int j = 0; j < n;j++)
if(i == j) map[i][j] = 0;
else map[i][j] = map[j][i] = inf;
//给各边关系赋值
for(int i = 0; i < m; i++)
{
cin >> a >> b >> c;
map[a][b] = map[b][a] = c;
//初始化结构体元素
Element[i].weight = c;
Element[i].begin = a;
Element[i].end = b;
}
for(int i = 0; i < n; i++)
id[i] = i;
}
//快速排序
int partition(Element1 Element[],int i,int j)
{
while(i < j)
{
while(i < j && Element[i].weight <= Element[j].weight) j--;
if(i < j )
{
swap(Element[i].weight,Element[j].weight);
swap(Element[i].begin,Element[j].begin);
swap(Element[i].end,Element[j].end);
}
while(i < j && Element[i].weight <= Element[j].weight) i++;
if(i < j )
{
swap(Element[i].weight,Element[j].weight);
swap(Element[i].begin,Element[j].begin);
swap(Element[i].end,Element[j].end);
}
}
return i;
}
void quick_sort(Element1 Element[],int i,int j)
{
if(i < j)
{
int priovt = partition(Element,i,j);
quick_sort(Element,i,priovt-1);
quick_sort(Element,priovt+1,j);
}
}
//并查集
//查找根结点
int find(int p)
{
while(p != id[p])
{
p = id[p];
}
return p;
}
//Kruskal算法
void Kruskal(Element1 Element[])
{
int begin = 0; //边开始结点
int end = 0; //边结束结点
//遍历所有的边
for(int i = 0; i < m; i++)
{
//获取最小边的两个结点,判断是否在同一个树中
begin = Element[i].begin;
end = Element[i].end;
//并查集中的并操作
if(find(begin) != find(end))
{
//一定要将根结点进行连接!!!
id[find(begin)] = find(end);
cout << "连接:" << begin << "-----" << end << endl;
}
}
}
//输出图关系
void getMap()
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
cout << map[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
int main(int argc, char** argv) {
while(cin >> n >> m)
{
Element1 Element[100];
init(Element);
// getMap();
quick_sort(Element,0,m-1);
cout << " 排序后:";
for(int i = 0; i < m; i++)
{
cout << Element[i].weight << " "
<< Element[i].begin << " " << Element[i].end << endl;
}
Kruskal(Element);
}
/* 测试用例
6 9
0 1 34
0 2 46
0 5 19
1 4 12
2 5 25
2 3 17
3 5 25
3 4 38
4 5 26
*/
return 0;
}