Prim算法:
假设有两个集合V和U,V存放图中已处理(已加入最小生成树)的顶点,U存放图中未处理的顶点。
每次从集合U中选取一个与 集合V中的顶点 之间权值最小的顶点。
例如初始时选定某一顶点a加入集合V,然后从集合U中选取一个与集合V中顶点之间权值最小的顶点,
此时V中只有a,所以就是选择U中与顶点a之间权值最小的顶点,
假设选中了顶点b,将其也加入集合V,此时最小生成树有a和b两个结点,
即集合V中有两个顶点a和b,所以要从U中寻找与顶点a或顶点b之间权值最小的顶点,
重复这个过程,直到集合U中不再有任何元素(或树的结点数等于图的顶点树)。
需要定义一个辅助数组来记录最小生成树。数组下标对应图的顶点数组的下标(意思是辅助数组的第 i 个位置代表图的第 i 个顶点),
数组每个元素为一个结构体,有两个成员,
成员1为另一个顶点的下标,即该顶点所连接的另一个顶点,亦即最小生成树中一个结点所连接的另一个结点;
成员2为两个顶点之间的权值。
举个栗子,假设在集合U中找到一个顶点u1,与集合V中的顶点v1之间权值最小,接下来则要获取顶点v1在顶点数组中的下标,假设是 i ,
则辅助数组中第 i 位元素需要赋值,其成员1即为顶点u1的下标,成员2即为顶点v1与顶点u1之间的权值。
//图结构的定义
typedef struct AMGraph
{
int vex[ARRAYSIZE]={0};//顶点数组
int arc[ARRAYSIZE][ARRAYSIZE];//邻接矩阵,表示顶点之间的关系
int vexnum=0,arcnum=0;//顶点个数和弧个数
int type=0;//1表示无向图,2表示有向图,3表示无向网,4表示有向网
}AMGraph;
//最小生成树
//辅助数组:其下标对应顶点数组的下标
typedef struct CloseDge
{
int adjvex=-1;//当前下标的顶点所连接的另一顶点
int lowcost=0;//两个顶点之间的权重
}CloseDge;
//Prim算法
void Prim(AMGraph &G,int start)
{
//从起始顶点start出发,生成图G的最小生成树
int Tnum=0;//树的结点数
CloseDge cd[G.vexnum];//建立辅助数组
//根据起始顶点初始化辅助数组
for(int i=0;i<G.vexnum;i++)
{
cd[i].adjvex=start;
cd[i].lowcost=G.arc[start][i];
}
cd[start].lowcost=0;//将已处理的顶点标记为0
++Tnum;//树结点增1
int mincost=0;//记录最小权值
int minvex=0;//记录最小权值的对应顶点
while(Tnum<G.vexnum)
{
for(int i=0;i<G.vexnum;i++)//遍历每一个顶点
{
if(cd[i].lowcost==0)//如果顶点i在集合V中
{
mincost=G.arc[i][0];//先假设顶点i与顶点0之间权值最小
for(int j=0;j<G.vexnum;j++)//遍历每一个顶点
{
if(cd[j].lowcost!=0)//非集合V的元素
{
if(G.arc[i][j]<=mincost)//如果权值更小
{
mincost=G.arc[i][j];//更小最小值
minvex=j;//更新下标
}
}
}
//将找到的最小权值与辅助数组中的对应位置的权值比较
if(cd[minvex].lowcost>mincost)//如果刚才找到的权值和对应顶点更小,则更新
{
cd[minvex].lowcost=mincost;
cd[minvex].adjvex=i;
}
}
}
mincost=INT_MAX;
for(int i=0;i<G.vexnum;i++)//遍历辅助数组,寻找权重最小的顶点
{
if(cd[i].lowcost!=0 && cd[i].lowcost<mincost)
{
mincost=cd[i].lowcost;
minvex=i;
}
}
//输出最小生成树
cout << "(" << G.vex[cd[minvex].adjvex] << "," << G.vex[minvex] << ")" << G.arc[cd[minvex].adjvex][minvex] << endl;
//将已处理的顶点标记为0
cd[minvex].lowcost=0;
Tnum++;
}
}
Kruskal算法:
对比Prim算法与Kruskal算法,前者是对点进行操作,后者则是对边进行操作。
首先先将图中的所有边进行递增排序,以便我们每次都能选出一条最短边,
如果它和当前的最小生成树不构成回路则将其加入最小生成树,否则将其删除。
然后继续选取最短边,直至所有边都处理完毕。
//图常用的邻接矩阵和邻接表都不适合用于该算法,需要重新定义一种存储结构。
//定义边结点
typedef struct EdgeNode
{
int v1=0,v2=0,weight=0;//边的两个顶点、权值
}EdgeNode;
//定义图结构
typedef struct myGraph
{
int vex[MAXVEX]={0};//顶点
EdgeNode edge[MAXEDGE];//边
int vexnum=0,edgenum=0;//顶点数、边数
}myGraph;
int LocateVex(myGraph &G,int e);获取顶点位置
int get_root(myGraph &G,int *parent,int e);//获取根结点(返回 值为e的顶点在最小生成树中的根结点的对应图结构中的顶点下标)
void creat_graph(myGraph &G);//创建无向网
//最小生成树
//使生成树以一种随意的方式生长,初始时每个顶点构成一棵树,之后每生长一次就将两棵树合并,
//直到图中所有顶点都合并为一棵树。
void Kruskal(myGraph &G)
{
//设置一个数组来存放每个顶点在树中的根结点,其下标对应顶点数组的下标,
int parent[G.vexnum];
for(int i=0;i<G.vexnum;++i) parent[i]=-1;//初始化数组
//根据权值排序边数组
for(int i=0;i<G.edgenum;++i)
for(int j=i+1;j<G.edgenum;++j)
if(G.edge[i].weight>G.edge[j].weight)
swap(G.edge[i],G.edge[j]);
int r1=-1,r2=-1;//获取顶点在树中的根结点
for(int n=0,i=0;i<G.vexnum-1;++n)//i表示最小生成树的边数,n个顶点只需n-1条边
{
r1=get_root(G,parent,G.edge[n].v1);
r2=get_root(G,parent,G.edge[n].v2);
if(r1!=r2)//若两个顶点没有相同的根结点
{
parent[LocateVex(G,r2)]=r1;//合并两棵树,即连接两个根结点
++i;//边数累加
cout << "(" << G.edge[n].v2 << "," << G.edge[n].v1 << ")" << G.edge[n].weight << endl;//输出生成树
}
//若两个顶点有相同的根结点,则不生成树,防止形成回路
}
}