Minimum spanning tree-Kruskal algorithm

Basic idea of ​​Kruskal's algorithm

Insert picture description here
Comparison of Prim's algorithm and Kruskal's algorithm:
Insert picture description here

Fake code

Insert picture description here
Insert picture description here

Data structure design

Insert picture description here
Insert picture description here
Connected component
Insert picture description here

Diagram

Note: Sorting the edge array according to the size of the weight is the premise of the algorithm
Insert picture description here

Insert picture description here
Insert picture description here

Insert picture description here
Insert picture description here
Insert picture description here

Minimum spanning tree algorithm

Insert picture description here

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;
}

Insert picture description here

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;
	}
}

Insert picture description here
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;
	}
     
}

Insert picture description here

Guess you like

Origin blog.csdn.net/m0_53157173/article/details/115056820
Recommended