图---最小生成树---普里姆算法

普里姆算法简介:

设集合U为最小生成树的顶点集合,开始时只有一个结点,这个结点是我们任意指定的,就从这个结点开始,以此向该结合中添加其余剩下的结点。

其规则如下:(设集合V包含图中所有的顶点)

1、找最小权值的边:该边的两个顶点分别属于集合U和集合V-U   (此时该边就是最小生成树的边

2、将该边的属于集合V-U的顶点并入集合U,从V-U集合中删除       (第 1  、2 步可以防止生成树形成回路

3、重复步骤1,直至集合U包含了图中的所有顶点

这样最小生成树就形成了,也就是所找到的所有边和所有顶点形成的树。若图有n个顶点,则上面的步骤循环了n-1次,因为n个顶点的无向连通图的最小生成树的边为n-1条


下面举例说明,给定的图,如下:

6个顶点和10条边!

此例为严版教材上的例子,书上给定的开始顶点为1,这里我们给定开始的顶点为6,具体步骤如下:



从顶点6开始



这样最小生成树就顺利找出来了,共用了5步,第一步为我们给定的一个结点。

————————————————————————————————————————————————————————————————————————————

关于对严版介绍的代码具体实现,具体如下:


#define MAX_INT		0x7fffffff	//最大权值
#define MAX_VERTEX_NUM	20


typedef struct ArcCell//顶点关系
{
	int adj;//顶点关系
	int weight;//边的权值
}AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];


typedef struct //集合V-U中顶点和集合U中顶点的关系,即指示两集合顶点之间最小权值的边
{
	int adjVex;//邻接点
	int lowCost;
}Edge;


class CGraph
{
public:
	int m_vex[MAX_VERTEX_NUM];//顶点向量
	AdjMatrix	m_arcs;//邻接矩阵
	int m_vexnum;//顶点个数
	int m_arcnum;//边或弧的条数


public:
	CGraph();
	~CGraph();


	int LocateVex(int vex);//定位
	bool CreateGraph();//创建图


	void MiniSpanTree_PRIM(int vex);//求最小生成树,输出边
	int Minimum(Edge edge[]);
	void PrintGraph();
};


#include "Graph.h"

#include<iostream>

using namespace std;

CGraph::CGraph()//构造函数
{

}

CGraph::~CGraph()//析构函数
{

}

int CGraph::LocateVex(int vex)
{
	for(int i = 0 ;i<m_vexnum;i++)
		if(m_vex[i] == vex)
			return i;
	return -1;//查找失败!
}

bool CGraph::CreateGraph()
{
	int i,j;
	cout<<"输入顶点个数:";
	cin>>m_vexnum;
	cout<<"输入边或弧的条数:";
	cin>>m_arcnum;

	cout<<"输入顶点数据:";
	
	for(i = 0;i < m_vexnum;i++)
	{
		cin>>m_vex[i];
	}
	cout<<endl;

	//*********初始化邻接矩阵
	for(i=0;i<m_vexnum;i++)
	{
		for(j=0;j<m_vexnum;j++)
		{
			m_arcs[i][j].adj = 0;//无边或弧相连
			m_arcs[i][j].weight = MAX_INT;//权值为0
		}
	}
	cout<<"输入边或弧的数据:\n";
	for(i = 0;i < m_arcnum;i++)
	{
		int vex1,vex2;
		int k1,k2;
		int weight;
		cin>>vex1>>vex2>>weight;;
		k1 = LocateVex(vex1);
		k2 = LocateVex(vex2);
		if(k1>-1 && k1<m_vexnum && k2>-1 && k2<m_vexnum)
		{
			m_arcs[k1][k2].adj = m_arcs[k2][k1].adj = 1;//无向图的初始化
			m_arcs[k1][k2].weight = m_arcs[k2][k1].weight = weight;
		}
		else//输入错误
		{
			cout<<"输入的顶点数据错误!!!\n";
			return false;
		}
	}
	return true;//创建成功
}

void CGraph::PrintGraph()
{
	int i,j;

	cout<<"顶点序列为:";
	for(i=0;i<m_vexnum;i++)
		cout<<m_vex[i]<<"  ";
	cout<<endl<<endl;

	cout<<"邻接关系为:\n";
	for(i=0;i<m_vexnum;i++)
	{
		for(j=0;j<m_vexnum;j++)
		{
			cout.width(5);//设置输出宽度
			cout<<m_arcs[i][j].adj<<"  ";
		}
		cout<<endl;
	}

	cout<<endl<<endl;
	cout<<"带权矩阵为:\n";
	
	
	for(i=0;i<m_vexnum;i++)
	{
		for(j=0;j<m_vexnum;j++)
		{
			cout.width(5);//设置输出宽度
			cout<<m_arcs[i][j].weight<<"  ";
		}
		cout<<endl;
	}
	
}

void CGraph::MiniSpanTree_PRIM(int vex)
{
	Edge edge[MAX_VERTEX_NUM];//定义一个结构体数组,用来实现集合U-V
	int i,j,k;
	k = LocateVex(vex);//定位
	for(i=0;i<m_vexnum;i++)//初始化辅助数组
		if(i != k)
		{
			edge[i].adjVex = vex;
			edge[i].lowCost = m_arcs[i][k].weight;
		}
	edge[k].lowCost = 0;	//初始 U = {vex}

	cout<<"最小生成树为:\n";
	for(i = 1;i<m_vexnum;i++)//选择剩下的顶点
	{
		k = Minimum(edge);//找到最小结点的下标
		cout<<m_vex[k]<<"  "<<edge[k].adjVex<<endl;		//输出最小生成树的边   当然我们可以根据实际需要在这步将边保存起来另作他用
		edge[k].lowCost = 0;//归并结点
		for( j = 0;j<m_vexnum;j++)//修改原来权值
		{
			
			if(m_arcs[k][j].weight < edge[j].lowCost )
			{
				edge[j].lowCost = m_arcs[k][j].weight;
				edge[j].adjVex = m_vex[k];
			}
		}
	}
}

int CGraph::Minimum(Edge edge[])//找最小边
{
	int minWeight;
	int v;
	int i;
	for(i =0;i<m_vexnum;i++)//初始最小权值的结点
	{
		if(edge[i].lowCost)//权值不为0
		{
			v = i;//得到最小结点的下标
			minWeight = edge[i].lowCost;
			break;
		}
	}
	//查找最小结点
	for(i++;i<m_vexnum;i++)
	{
		if(edge[i].lowCost !=0 && edge[i].lowCost < minWeight)
		{
			v = i;
			minWeight = edge[i].lowCost;
		}
	}//for
	
	return v;//返回最小结点所在下标
}

int main()
{
	CGraph g;//声明一个图对象
	g.CreateGraph();//创建一个图

	g.MiniSpanTree_PRIM(6);//求最小生成树
	cin.get();
	return 0;
}



创建图时所使用的数据为:

6
10
1 2 3 4 5 6
1 2 6
1 3 1
1 4 5
2 3 5
3 4 5
2 5 3
3 5 6
3 6 4
4 6 2

5 6 6

------对于边的数据:前两个数据代表顶点,最后一个数据代表权值,如 5 6 6表示顶点5和顶点6之间边的权值为6

程序运行时可直接复制上去。其运行结果见下图:



输出的最小生成树为边

---------------------------------------------------------------------------------------------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值