最小生成树Prim算法和Kruskal算法

一、最小生成树概念与应用

  1. 最小生成树的概念 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n个结点,并且有保持图连通的最少的边。

  2. 最小生成树的性质(MST性质) 假设N=(V,{E}) 是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,其中u∈U,v∈V-U
    ,则必存在一棵包含边(u,v)的最小生成树。

  3. 最小生成树的应用 假设要在n个城市之间建立通信联络网,在每两个城市之间都可以设置一条线路,相应地都要付出一定的经济代价。要使这 n 个城市的任意两个之间都可以通信,并且总的耗费最少,就要找到带权的最小生成树。

二、普里姆(Prim)算法思路

假设 N= (V,{E})是连通网,TE是N上最小生成树中边的集合。初始令U={uo}(uo∈V),TE={}, 在所有u∈U,v∈V一U的边 (u,v)∈E中找一条代价最小的边 (uo,vo)并入集合TE,同时vo并入U。重复执行上述操作直至U=V为止,则T= (V,{TE})为N的最小生成树。
每次迭代选择代价最小的边对应的,加入到最小生成树中。算法从某一个顶点S开始,逐渐长大覆盖整个连通网的所有顶点。所以此算法也被称为“加点法”。

#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<string.h>
using namespace std;

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int VRType;
typedef int VertexType;

#define INFINITY INT_MAX
#define MAX_VERTEX_NUM 20
typedef struct ArcCell
{
	VRType adj;
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

typedef struct
{
	VertexType vexs[MAX_VERTEX_NUM];//节点数值 
	AdjMatrix arcs;//邻接矩阵 
	int vexnum, arcnum;//节点数,边数 
}MGraph;

typedef struct Array
{
	VertexType adjvex;//上一个点 
	VRType lowcost;	//最小权重 
}closedge[MAX_VERTEX_NUM];

int LocateVex(MGraph G, VertexType v)
{
	int i = 0;
	for(i; i < G.vexnum; i++)
	{
		if(v == G.vexs[i])
		{
			break;
		}
	}
	if(i < G.vexnum)
	{
		return i;
	}else
	{
		return G.vexnum ;
	}
}
Status CreateUDN(MGraph &G)
{
	cout<<"输入顶点数以及边数(以空格分隔):"<<endl;
	cin>>G.vexnum>>G.arcnum; 
	cout<<"请输入顶点信息(以空格分隔):"<<endl;
	for(int i = 0; i < G.vexnum; ++i)
	{
		cin>>G.vexs[i];
	}
	for(int i = 0; i < G.vexnum; i++)
	{
		for(int j = 0; j < G.vexnum; j++)
		{
			G.arcs[i][j] = {INFINITY};
		}
	}
	cout<<"请输入边的信息v1,v2,w(以空格分隔):"<<endl;
	for(int k = 0; k < G.arcnum; k++)
	{
		VertexType v1, v2;
		VRType w = INT_MAX; 
		cin>>v1>>v2>>w;
		
		int i = 0, j = 0;
		i = LocateVex(G, v1);
		j = LocateVex(G, v2);
		if(i == G.vexnum) exit(0);
		if(j == G.vexnum) exit(0);
		
		G.arcs[i][j].adj = w;
		G.arcs[j][i].adj = G.arcs[i][j].adj;
	}
	return OK;
}

void PrintMGraph(MGraph G) 
{
    int i;
    for (i = 0; i < G.vexnum; i++)
	{

        for (int j = 0; j < G.vexnum; j++) 
		{
            if (G.arcs[i][j].adj == INT_MAX)
                cout << "∞" << " ";
            else
            cout << G.arcs[i][j].adj << " ";
        }
        cout << endl;
    }
}

int Minimum(closedge C)
{
	int i = 0;
	int mincost = INT_MAX;
	int k = -1;
	for(i; i < MAX_VERTEX_NUM; i++)
	{
		if(C[i].lowcost != 0)
		{
			if(C[i].lowcost < mincost)
			{
				mincost = C[i].lowcost;
				k = i;
			}
		}
	}
	return k;
}

void Prim(MGraph G, VertexType u)
{
	closedge closedge = {0, 0};
	int k = LocateVex(G, u);
	int j = 0;
	//初始化辅助矩阵 
	for(j; j < G.vexnum; j++)
	{
		if(j != k)
		{
			closedge[j] = {u, G.arcs[k][j].adj};
		}
	}
	closedge[k].lowcost = 0;
	
	int i = 1;
	for(i; i < G.vexnum; i++)
	{
		k = Minimum(closedge);
		cout<<"("<<closedge[k].adjvex<<","<<G.vexs[k]<<")"<<" ";
		closedge[k].lowcost = 0;
		
		for(int f = 0; f < G.vexnum; f++)
		{
			if(G.arcs[k][f].adj < closedge[f].lowcost)
			{
				closedge[f] = {G.vexs[k], G.arcs[k][f].adj};
			}
		}
	}
}

int main()
{
	MGraph G;
	CreateUDN(G);
	cout<<"矩阵为:"<<endl;
	PrintMGraph(G);
	cout<<"最小生成树为:"<<endl;
	Prim(G, 1);
	return 0;
}

运行结果示例:
在这里插入图片描述

三、克鲁斯卡尔(Kruskal)算法思路

假设N=(V,{E})是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图T={V,{}},图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上(即:不形成环),则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直至T中所有顶点都在同一连通分量上为止。
在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。所以此算法也被称为“加边法”。

#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<string.h>
using namespace std;

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int VRType;
typedef int VertexType;

#define INFINITY INT_MAX
#define MAX_VERTEX_NUM 20
typedef struct ArcCell
{
	VRType adj;
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

typedef struct
{
	VertexType vexs[MAX_VERTEX_NUM];//节点数值 
	AdjMatrix arcs;//邻接矩阵 
	int vexnum, arcnum;//节点数,边数 
}MGraph;

int LocateVex(MGraph G, VertexType v)
{
	int i = 0;
	for(i; i < G.vexnum; i++)
	{
		if(v == G.vexs[i])
		{
			break;
		}
	}
	if(i < G.vexnum)
	{
		return i;
	}else
	{
		return G.vexnum ;
	}
}
Status CreateUDN(MGraph &G)
{
	cout<<"输入顶点数以及边数(以空格分隔):"<<endl;
	cin>>G.vexnum>>G.arcnum;
	cout<<"请输入顶点信息(以空格分隔):"<<endl; 
	for(int i = 0; i < G.vexnum; ++i)
	{
		cin>>G.vexs[i];
	}
	for(int i = 0; i < G.vexnum; i++)
	{
		for(int j = 0; j < G.vexnum; j++)
		{
			G.arcs[i][j] = {INFINITY};
		}
	}
	cout<<"请输入边的信息v1,v2,w(以空格分隔):"<<endl;
	for(int k = 0; k < G.arcnum; k++)
	{
		VertexType v1, v2;
		VRType w = INT_MAX; 
		cin>>v1>>v2>>w;
		
		int i = 0, j = 0;
		i = LocateVex(G, v1);
		j = LocateVex(G, v2);
		if(i == G.vexnum) exit(0);
		if(j == G.vexnum) exit(0);
		
		G.arcs[i][j].adj = w;
		G.arcs[j][i].adj = G.arcs[i][j].adj;
	}
	return OK;
}

void PrintMGraph(MGraph G) 
{
    int i;
    for (i = 0; i < G.vexnum; i++)
	{

        for (int j = 0; j < G.vexnum; j++) 
		{
            if (G.arcs[i][j].adj == INT_MAX)
                cout << "∞" << " ";
            else
            cout << G.arcs[i][j].adj << " ";
        }
        cout << endl;
    }
}

typedef struct Arr
{
	VertexType strat, end;
	VRType weight;
}WEdge[MAX_VERTEX_NUM * (MAX_VERTEX_NUM - 1) / 2 + 1];

int Partition(WEdge edge, int low, int high)
{
	Arr a = {0, 0, INT_MAX};
	a = edge[low];
	int pivotkey = edge[low].weight;

	while(low<high)
	{
		while(low<high && edge[high].weight >= pivotkey)
		{
			--high;
		}
		edge[low] = edge[high];
		while(low<high && edge[low].weight <= pivotkey)
		{
			++low;
		}
		edge[high] = edge[low];
	}
	edge[low] = a;
	return low;
}

void QSort(WEdge edge, int low, int high)
{
	if(low < high)
	{
		int pivotloc = Partition(edge, low, high);
		QSort(edge, low, pivotloc - 1);
		QSort(edge, pivotloc + 1, high);
	}
}
void Kruskal(MGraph G)
{
	//把边的权重存到数组里 
	int j = 0;
	int k = 0;
	int sum = 0;
	WEdge edge = {0, 0, INT_MAX};
	
	for(k; k < G.vexnum; k++, j++)
	{
		for(int i = 0; i < j; i++)
		{
			if(G.arcs[j][i].adj > 0 && G.arcs[j][i].adj != INT_MAX)
			{
				edge[sum] = {j, i, G.arcs[j][i].adj};
				sum++; 
			}
			
		}
	}
	
	QSort(edge, 0, G.arcnum - 1);
	
	int count3 = 0;
	int parents[G.vexnum];
	for(count3; count3 < G.vexnum; count3++)
	{
		parents[count3] = count3;
	}
	
	int arcsum = 1;//生成树里边的个数
	int count4 = 0; 
	while(arcsum < G.arcnum)
	{
		int m1 = edge[count4].strat;
		int m2 = edge[count4].end;
		int sn1 = parents[m1];
		int sn2 = parents[m2];
		if(sn1 != sn2)
		{
			cout<<"("<<G.vexs[m2]<<","<<G.vexs[m1]<<") "<<edge[count4].weight<<" ";
			arcsum++;
			if(arcsum >= G.vexnum) break;
			for(int i = 0; i <= G.vexnum; i++)
			{
				if(parents[i] == sn2)
				{
					parents[i] = sn1;
				}
			}
		}
		count4++;
	}
		
}


int main()
{
	MGraph G;
	CreateUDN(G);
	cout<<"矩阵为:"<<endl;
	PrintMGraph(G);
	cout<<"最小生成树为:"<<endl;
	Kruskal(G);
	return 0;
}

运行结果示例:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值