在无向图中,若任意两个顶点vi与vj都有路径相通,则称该无向图为连通图。(强连通图:在有向图中,若任意两个顶点vi与vj都有路径相通,则称该有向图为强连通图。)
在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。 一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
prim算法:
此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。
1.图的所有顶点集合为V;初始令集合u={s},v=V−u;
2.在两个集合u,v能够组成的边中,选择一条代价最小的边(u0,v0),加入到最小生成树中,并把v0并入到集合u中。
3.重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。
Krustral算法:
此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
1. 把图中的所有边按代价从小到大排序;
2. 把图中的n个顶点看成独立的n棵树组成的森林;
3. 按权值从小到大选择边,所选的边连接的两个顶点ui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
//注释的代码本来是想用指针写prim算法,可是比较尴尬没写出来。
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define MaxInt 1000000
#define MVNum 100
#define Vexcount 4
using namespace std;
typedef unsigned int ArcType;//边的权值类型
typedef int VerTexType; //顶点的权值类型
typedef struct
{
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MVNum];//邻接矩阵
int vexnum,arcnum; //图的当前点数和边数
} AMGraph;
//typedef struct ArcNode //边结点
//{
// int adjvex; //该边所指向的顶点的位置
// int Valuel; //该边的权值
// struct ArcNode*nextarc; //指向下一条边的指针
//} ArcNode;
//typedef struct VNode
//{
// VerTexType data; //顶点信息
// ArcNode *firstarc; //指向第一条依附该顶点的边的指针
//} VNOde,AdjList[MVNum]; //AdjList表示邻接表类型
//typedef struct
//{
// AdjList vertices; //邻接表
// int vexnum,arcnum; //图的当前顶点数和边数
//} ALGraph;
struct node
{
ArcType adjvex; //最小边在U中的那个顶点
ArcType lowcost; //最小边上的权值
} closedge[MVNum]; //辅助数组,用来记录从顶点集U到V-U的权值最小的边
struct nod
{
VerTexType Head; //边的始点
VerTexType Tail; //边的终点
ArcType lowcost; //边上的权值
} Edge[MVNum];
int Vexset[MVNum];
//int vis[MVNum];
bool cmp(nod Edge1,nod Edge2)
{
return Edge1.lowcost<Edge2.lowcost;
}
int LocateVex(AMGraph G,int u)
{
return u;
}
//int LocateVex(ALGraph G,int u)
//{
// return u;
//}
void CreatUDN(AMGraph &G)
{
//采用邻接矩阵表示法,创建无向网G
int i,j,k,v1,v2,w;
cin>>G.vexnum>>G.arcnum; //输入总顶点数,总边数。
for( i=1; i<=Vexcount; ++i)
cin>>G.vexs[i]; //依次输入点的信息
for( j=0; j<=G.vexnum; ++j) //初始化邻接矩阵,边的权值均置为MaxInt
for( i=1; i<=G.vexnum; ++i)
G.arcs[i][j]=MaxInt;
for( k=0; k<G.arcnum; ++k) //构造邻接矩阵
{
cin>>v1>>v2>>w; //输入一条边依附的顶点及权值
i=LocateVex(G,v1);
j=LocateVex(G,v2);
Edge[k].Head=i;
Edge[k].Tail=j;
Edge[k].lowcost=w;
G.arcs[i][j]=w; //边<v1,v2>的权值置为w
G.arcs[j][i]=G.arcs[i][j];//置<v1,v2>的对称边<v2,v1>的权值为w
}
}//时间复杂度O(n^2)
//void CreatUDG(ALGraph &G)
//{
// 采用邻接表表示法,创建无向网G
// int i,k,v1,v2,w,j;
// ArcNode *p1,*p2;
// cin>>G.vexnum>>G.arcnum; //输入总顶点数,总边数
// for( i=1; i<=G.vexnum; ++i) //输入各点构造表头结点表
// {
// cin>>G.vertices[i].data; //输入顶点值
// G.vertices[i].firstarc=NULL;//初始化表头结点的指针域为NULL
// }
// for( k=1; k<=G.arcnum; ++k) //输入各边,构造邻接表
// {
// cin>>v1>>v2>>w; //输入一条边依附的两个顶点
// i=LocateVex(G,v1);
// j=LocateVex(G,v2);
// p1=new ArcNode; //生成一个新的边结点*p1
// p1->adjvex=j; //邻接点序号为j
// p1->Valuel=w; //边的权值
// p1->nextarc=G.vertices[i].firstarc;
// G.vertices[i].firstarc=p1;
// 将新结点*p1插入顶点vi的头部
// p2=new ArcNode; //生成另一个对称的新的边结点*p2
// p2->adjvex=i; //邻接点序号为i
// p2->Valuel=w; //边的权值
// p2->nextarc=G.vertices[j].firstarc;
// G.vertices[j].firstarc=p2;
// 将新结点*p2插入顶点vj的头部
// }
//}//时间复杂度O(n+e)
int Min(struct node *closedge)
{
unsigned int Min = MaxInt;
int index = -1;
for (int i = 1; i <=Vexcount; i++)
{
if (closedge[i].lowcost < Min && closedge[i].lowcost !=0)
{
Min = closedge[i].lowcost;
index = i;
}
}
return index;
}
void MiniSpanTree_Prim(AMGraph G,VerTexType u)
{
int i,j,k,u0,v0,ans=0;
//无向网G以邻接矩阵形式存储,从顶点u出发构造G的最小生成树T,输出T的各条边
k=LocateVex(G,u);
for( j=1; j<=G.vexnum; ++j)
{
if(j!=k)
{
closedge[j].adjvex=k;
closedge[j].lowcost=G.arcs[k][j];
}
}
closedge[k].lowcost=0; //初始
closedge[k].adjvex=k;
for( i=2; i<=G.vexnum; ++i) //选择其余n-1个顶点,生成n-1条边
{
k=Min(closedge);
//求出T的下一个结点:第k个顶点,closedge[k]中存有当前最小边
u0=closedge[k].adjvex;
v0=G.vexs[k];
ans+=closedge[k].lowcost;
cout<<u0<<"=="<<v0<<endl;
closedge[k].lowcost=0;
for( j=1; j<=G.vexnum; ++j)
if(G.arcs[k][j]<closedge[j].lowcost)
{
closedge[j].adjvex=k;
closedge[j].lowcost=G.arcs[k][j];
}
}
cout<<"MiniSpanTree_Prim answer="<<ans<<endl;
}
//void MiniSpanTree_ListPrim(ALGraph G,VerTexType u)
//{
// unsigned int Min=MaxInt;
// int i,j,k,ans=0,index=-1;
// memset(vis,0,sizeof(vis));
// k=u;
// for(i=1; i<G.vexnum; ++i)
// {
// Min=MaxInt;
// index=-1;
// ArcNode *p;
// for( p=G.vertices[k].firstarc; p!=NULL; p=p->nextarc)
// {
// j=p->adjvex;
// if(p->Valuel <Min&&vis[j]==0)
// {
// vis[j]=1;
// Min=p->Valuel;
// index=j;
// }
// }
// for(j=1;j<G.vexnum;j++){
// if(!vis[j]&&>map[k][j])
// dis[j]=map[k][j];//更新最短距离
// cout<<k<<"-**-"<<index<<endl;
// ans+=Min;
// k=index;
// }
// cout<<"MiniSpanTree_ListPrim answer="<<ans<<endl;
//}
void MiniSpanTree_Kruskal(AMGraph G)
{
int i,j,v1,v2,vs1,vs2,ans=0;
//无向网G以邻接矩阵形式存储,构造G的最小生成树T,输出T的各条边
sort(Edge,Edge+G.arcnum,cmp); //将数组Edge中的元素按权值从小到大排序
for( i=0; i<=G.vexnum; ++i) //辅助数组,表示各顶点自成一个连通分量
Vexset[i]=i;
for( i=0; i<G.arcnum; ++i)
{
//依次查看排好序的数组Edge中的边是否在同一连通分量上
v1=LocateVex(G,Edge[i].Head); //v1为边的始点
v2=LocateVex(G,Edge[i].Tail); //v2位边的终点
vs1=Vexset[v1]; //获取v1所在连通分量
vs2=Vexset[v2]; //获取v2所在连通分量
if(vs1!=vs2) //两个顶点分属不同的连通分量
{
ans+=Edge[i].lowcost;
cout<<Edge[i].Head<<"--"<<Edge[i].Tail<<endl; //输出此边
for(j=1; j<=G.vexnum; ++j) //合并vs1和vs2两个分量。即两个集合统一编号
if(Vexset[j]==vs2) Vexset[j]=vs1; //集合编号为vs2的都改为vs1
}
}
cout<<"MiniSpanTree_Kruskal answer="<<ans<<endl;
}
int main()
{
AMGraph G;
// ALGraph G1;
CreatUDN(G);
// CreatUDG(G1);
MiniSpanTree_Prim(G,1);
// MiniSpanTree_ListPrim(G1,1);
MiniSpanTree_Kruskal(G);
}