-
连通性:若无向图的边有权值,则成该无向图为无向网;若无向网中每个顶点都相通,称为连通网。
-
支撑树:连通图G的某一无环连通子图T若能覆盖G中所有顶点,则称T为G的一棵支撑树或生成树。
-
最小支撑树:若图G为一带权网络,则每一棵支撑树的成本为其所拥有的各边的权重的总和。在G的所有支撑树中,成本最低的称为最小支撑树。
一、普利姆算法
在图 G = ( V , E ) G=(V,E) G=(V,E)中,顶点集 V V V的任一子集 U U U及其补集 V − U V-U V−U都构成 G G G的一个割,记作 ( U : V (U:V (U:V- U ) U) U)。若边 u u u、 v v v满足 u ∈ U u\in U u∈U且 v ∉ U v\not\in U v∈U,则称作该割的一条跨越边。跨越边连接 V V V与 V V V的补集,故可将其形象地看作该割的一座桥。而Prim算法基于以下事实:最小支撑树总是会采用联结每一割的最短跨越边。
先假设 G = ( V , E ) G=(V,E) G=(V,E)是连通网, T E TE TE为 G G G上最小生成树中边的集合:
- U = u 0 , ( u 0 ∈ V ) , T E = { } U={u_0},(u_0\in V),TE=\{\} U=u0,(u0∈V),TE={}
- 在所有的 u ∈ U , v ∈ V − U u\in U,v\in V-U u∈U,v∈V−U的边 ( u , v ) ∈ E (u,v)\in E (u,v)∈E中找一条代价最小的边 ( u , v 0 ) (u,v_0) (u,v0)并入集合 T E TE TE,同时 v 0 v_0 v0并入 U U U。
- 重复2,直到 U = V U=V U=V
实现:(基于矩阵的无向带权网络)
void minspantree_prim(int v){ //v为生成树的起始点
int k,j,i;
int low=10000;
struct{
int adj; //指示是由哪个顶点到该顶点
int lowcost; //存储最小权值
}closedge[MAXVERTEXNUM]; //用数组存储到各顶点的最小路径权值,使用数组下标指示到某顶点
k=v; //先使用v,先求由v到其他各顶点的最短路径
closedge[v].lowcost=0;
for(j=0;j<VertexNum;j++){ //初始化,将数组中的元素表示为由k到各顶点的路径权值
if(j!=k){
closedge[j].adj=k;
if(AdjMatrix[k][j]==0)
closedge[j].lowcost=10000;
else
closedge[j].lowcost=AdjMatrix[k][j];
}
}
for(i=1;i<VertexNum;i++){
low=10000;
for(j=0;j<VertexNum;j++){ //找出当前点集数组所有路径的最小权值路径
if(low>closedge[j].lowcost && closedge[j].lowcost!=0)
{
low=closedge[j].lowcost;
k=j;
}
cout<<closedge[k].adj<<" "<<k<<" "<<closedge[k].lowcost<<endl;
closedge[k].lowcost=0; //使用lowcost=0表示该点已访问过
for(j=0;j<VertexNum;j++) //使用上一步骤中找到的顶点来更新点集最短权值路径
{
if(closedge[j].lowcost>AdjMatrix[k][j] && AdjMatrix[k][j]!=0 && k!=j)
{
closedge[j].adj=k;
closedge[j].lowcost=AdjMatrix[k][j];
}
}
}
}
}
以上代码的关键为定义结构:
struct Arc{
int adj;
int lowcost;
}
再定义结构数组:
Arc closedge[MAXVERTEXNUM];
以closedge[i].adj
的方式存储由adj
指向i
的路径权值。
二、克鲁斯卡尔算法
不断地从图中选取最小权重且不会使树含有环的路径,生成搜索树,直到图中所有顶点都在搜索树上。
实现细节: 使用数组标识当前加入搜索树的两个点是否属于同一阵营,若是,则该路径加入搜索树会产生环。
算法实现:
void Krus(){
int *group=new int[VertexNum]; //使用数组标识阵营信息
for(int i=0;i<VertexNum;i++){
group[i]=i;
}
int matrix[MAXVERTEXNUM][MAXVERTEXNUM]; //创建临时的矩阵,后续的操作会对矩阵进行修改
for(int i=0;i<VertexNum;i++){
for(int j=0;j<VertexNum;j++){
matrix[i][j]==AdjMatrix[i][j];
}
}
for(int i=0;i<VertexNum-1;i++){
int from=-1,to=-1;
shortest(from,to,group); //找出当前权重最小的边
cout<<Vertex[from]<<" "<<Vertex[to]<<" "<<AdjMatrix[from][to]<<endl; //起点 终点 权重
matrix[from][to]=2*INFINITY; //将已加入搜索树的路径权值设为无穷大,以防止被再次访问
matrix[to][from]=2*INFINITY;
for(int x=0;x<VertexNum;x++){ //将所有from同阵营的顶点加到与to同阵营
if(group[x]==group[from] && x!=from)
group[x]=group[to];
}
set[from]=set[to];
}
}