普里姆算法简介:
设集合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;
}
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
程序运行时可直接复制上去。其运行结果见下图:
输出的最小生成树为边
---------------------------------------------------------------------------------------------