Basic idea of Kruskal's algorithm
Comparison of Prim's algorithm and Kruskal's algorithm:
Fake code
Data structure design
Connected component
Diagram
Note: Sorting the edge array according to the size of the weight is the premise of the algorithm
Minimum spanning tree algorithm
Complete code
#include<iostream>
using namespace std;
#define MaxVertex 10//最大顶点数为10
#define MaxEdge 100//最大边数为100
typedef int DataType;//顶点数组存储的顶点数据类型
//边集结构体
struct EdgeType
{
int from, to;//记录起始和结束顶点的下标
int weight;//边上的权值
};
//边集数组
class EdgeGraph
{
private:
DataType vertex[MaxVertex];//顶点数组
EdgeType edge[MaxEdge];//边集数组
int vertexNum;//顶点个数
int EdgeNum;//边的个数
public:
EdgeGraph(DataType v[],int n,int e);
//对边集数组进行排序
//1.快速排序
void quick_sort(int begin,int end);
//克鲁斯卡尔算法
void Kruskal();
};
EdgeGraph::EdgeGraph(DataType v[], int n, int e)
{
//初始化顶点个数
vertexNum = n;
//初始化边的个数
EdgeNum = e;
//初始化顶点数组
for (int i = 0; i < vertexNum; i++)
{
vertex[i] = v[i];
}
//初始化边集数组
for (int i = 0; i < EdgeNum; i++)
{
edge[i].from = -1;
edge[i].to = -1;
edge[i].weight = 0;
}
//人工给边集数组赋值赋值
for (int i = 0; i < EdgeNum; i++)
{
cin >> edge[i].from>>edge[i].to>>edge[i].weight;
}
}
//交换
void swap(int& v1, int& v2)
{
int temp = v1;
v1 = v2;
v2 = temp;
}
//交换排序
int swap_sort(EdgeType r[], int begin, int end)
{
//键值前面元素都比键值小,键值后面元素都比键值大
int i = begin;//设置起初begin位置的值为键值
int j = end;
//i==j时退出循环
while (i < j)
{
while (i < j && r[i].weight <= r[j].weight)
{
j--;//通过while循环,从end位置开始向后不断搜索,直到找到一个比r[i]小的值,或者i=j
}
//找到比r[i]小的值,就进行交换----交换过后r[j]是键值
if (i < j)
{
swap(r[i], r[j]);
//因为当前r[i]位置已经比较过了,下一次进入i移动的while循环时,可以不必再次比较当前r[i]
i++;
}
while (i < j && r[i].weight <= r[j].weight)
{
i++;//通过while循环,从begin位置开始向前不断搜索,直到找到一个比r[j]大的值,或者i=j
}
//找到比r[j]大的值,就进行交换----交换过后r[i]是键值
if (i < j)
{
swap(r[i], r[j]);
//因为当前r[j]位置已经比较过了,下一次进入j移动的while循环时,可以不必再次比较当前r[j]
j--;
}
}
return i;
}
//对边集数组进行快速排序
void EdgeGraph::quick_sort(int begin,int end)
{
int pos = 0;//记录当前键值的位置
if (begin < end)//数组中不止一个元素
{
pos = swap_sort(edge, begin, end);//对当前区间数组进行排序,返回排序完成后的键值
quick_sort(begin, pos - 1);//对当前键值前半部分进行排序
quick_sort(pos + 1, end);//对当前键值后半部分进行排序
}
}
//找到所在树的根节点:父母的数组 顶点下标
int findRoot(const int parent[],const int& vex)
{
int x = vex;
//如果当前传入的顶点就是根节点,那么我们需要返回他的顶点值,不然就会返回-1
while (parent[x] >-1)
{
x = parent[x];
}
return x;
}
void EdgeGraph::Kruskal()
{
//定义parent数组
int parent[MaxVertex];
//初始化parent数组
for (int i = 0; i < vertexNum; i++)
{
parent[i] = -1;
}
//先对边集数组进行排序
quick_sort(0, EdgeNum-1);
//测试--------打印边集数组
cout << "边集数组打印:" << endl;
cout << "from: " << " to: " << " weight: " << endl;
for (int i = 0; i < EdgeNum; i++)
{
cout <<edge[i].from<<"\t"<<edge[i].to<<"\t"<<edge[i].weight << endl;
}
//遍历边集数组
cout << "从起点开始的最小生成树:" << endl;
int vex1, vex2;//vex1---起始顶点 vex2----结束顶点
//num在这里是用来计算当前循环了几次的
for (int i = 0,num=0; i < EdgeNum; i++)
{
vex1 =findRoot( parent,edge[i].from);//得到起始顶点的所在树根节点
vex2 = findRoot(parent,edge[i].to);//得到结束顶点所在树的根节点
//两个顶点的根节点不同,不会构成环
if (vex1 != vex2)//满足条件说明此时要添加边
{
//输出加入边的格式
cout << "("<<edge[i].from << "," << edge[i].to << ")" << endl;
//设置起始顶点的根节点是结束顶点根节点的父亲
parent[vex2] = vex1;//合并树
num++;
if (num == vertexNum - 1)//循环了图的顶点数-1次,提前返回
return;
}
}
}
//测试------------------
void test()
{
DataType v[6] = {
0,1,2,3,4,5 };
EdgeGraph p(v, 6, 9);
p.Kruskal();
}
int main()
{
test();
system("pause");
return 0;
}
Different ways to sort the edge set array:
1. Quick sort
//交换
void swap(int& v1, int& v2)
{
int temp = v1;
v1 = v2;
v2 = temp;
}
//交换排序
int swap_sort(EdgeType r[], int begin, int end)
{
//键值前面元素都比键值小,键值后面元素都比键值大
int i = begin;//设置起初begin位置的值为键值
int j = end;
//i==j时退出循环
while (i < j)
{
while (i < j && r[i].weight <= r[j].weight)
{
j--;//通过while循环,从end位置开始向后不断搜索,直到找到一个比r[i]小的值,或者i=j
}
//找到比r[i]小的值,就进行交换----交换过后r[j]是键值
if (i < j)
{
swap(r[i], r[j]);
//因为当前r[i]位置已经比较过了,下一次进入i移动的while循环时,可以不必再次比较当前r[i]
i++;
}
while (i < j && r[i].weight <= r[j].weight)
{
i++;//通过while循环,从begin位置开始向前不断搜索,直到找到一个比r[j]大的值,或者i=j
}
//找到比r[j]大的值,就进行交换----交换过后r[i]是键值
if (i < j)
{
swap(r[i], r[j]);
//因为当前r[j]位置已经比较过了,下一次进入j移动的while循环时,可以不必再次比较当前r[j]
j--;
}
}
return i;
}
//对边集数组进行快速排序
void EdgeGraph::quick_sort(int begin,int end)
{
int pos = 0;//记录当前键值的位置
if (begin < end)//数组中不止一个元素
{
pos = swap_sort(edge, begin, end);//对当前区间数组进行排序,返回排序完成后的键值
quick_sort(begin, pos - 1);//对当前键值前半部分进行排序
quick_sort(pos + 1, end);//对当前键值后半部分进行排序
}
}
2. Insertion sort
//对边集数组进行快速排序
void EdgeGraph::insert_sort(int begin,int end)
{
for (int i = 1; i < EdgeNum; i++)
{
int j = i - 1;//j记录的是有序序列的最后一个元素
EdgeType temp = edge[i];//保存无序序列中的第一个元素
//从有序序列的最后一个元素开始,与temp的权值进行比较,直到找到权值比temp小的元素退出循环
while (temp.weight<edge[j].weight)
{
//每一次循环,都把当前遍历的j位置的值往前移动一次,目的是为了空出一个正确的位置来存放temp
edge[j + 1] = edge[j];
j--;
}
//while循环结束后,j指向的位置是遍历到比temp小的位置,temp要插入到当前j指向位置的前面
edge[j + 1] = temp;
}
}
3. Split Insertion Sort
//对边集数组进行折半插入排序
void EdgeGraph::insert_sort(int begin,int end)
{
int i;
EdgeType x;//用来存储无序序列的第一个元素
int mid;//指向有序序列中间的一个元素
int low, high;//指向有序序列的开头和结尾
//i每一次指向无序序列的第一个元素
for (int i = 1; i < EdgeNum; i++)
{
x = edge[i];//无序序列的第一个元素
low = 0;
high = i - 1;//有序序列最后一个元素
//找正确位置
while (low <= high)
{
mid = (low + high) / 2;//有序序列的中间值
//如果当前的无序序列的第一个x的权值大于等于有序序列中间值的权值
if (x.weight >=edge[mid].weight)
{
//更新比较区间
low = mid + 1;
}
else
{
high = mid - 1;
}
}
//找到要插入的地方时,要对插入地方前面的值进行移动
//j=i-1指向有序序列最后一个值
//退出while循环时,high的下标是要插入位置的前一个位置,把从high前面第一个元素开始的值的往前移动一个位置,直到覆盖当前无序序列第一个元素i的位子
int j;
for ( j = i - 1; j > high; j--)
{
edge[j+1] = edge[j];
}
//j此时指向的还是要插入元素的位置
//移动完后,进行插入
edge[j+1] = x;
}
}