运营商的挑战
-在下图标出的城市间架设一条通信线路
要求:
·任意两个城市间都能够通信
·将架设成本呢将至最低
如何在图中选择n-1条边使得n个顶点间两两可达,并且这n-1条边的权值之和最小
最小生成树
-仅使用图中的n-1条边连接图中的n个顶点
-不能使用产生回路的边
-个边上的权值的总和达到最小
最小生成树算法步骤
-选择某一顶点V0作为起始顶点,是的T={V0},F={V1,V2,...Vn},E={ }
-每次选择一条边,这条边是所有(u,v)中权值最小的边,且u属于T,v属于F
-修改T,F,E:
T = T + {v} ,F = F - {v}, E = E + {(u,v)}
-当F != NULL是,切(u,v)存在,转2;否则,结束
最小生成树算法的原材料
类型 | 变量 | 用途 |
Array<bool> | mark | 标记顶点所属的集合(T or F) |
Array<E> | cost | 记录T集合到F集合中顶点的最小权值 |
Array<int> | adjVex | 记录cost中权值的对应顶点 |
Queue<Edge> | ret | 记录最小生成树中的边 |
注意事项
-最小生成树仅针对无向图有意义
-必须判断图对象是否能够看做无向图
图类型(Graph)中的新增成员函数
-virtual bool isAdjacent (int i,int j) = 0;
-bool assUndirected();
·判断当前的有向图是否能够看做无向图
bool isAdjacent(int i ,int j)
{
return (0 <= i) && ( i < vCount()) && ( 0 <= j) && (j < vCount()) && (m_list.get(i)->edge.find(Edge<E>(i,j)) >=0);
}
bool asUndirected()
{
bool ret = true;
for(int i = 0;i<vCount() ; i++)
{
for(int j = 0;j<vCount();j++)
{
if(isAdjacent(i,j))
{
ret = ret && isAdjacent(j,i) && (getEdge(i,j) == getEdge(j,i));
}
}
}
return ret;
}
SharedPointer <Array <Edge <E> >> prim( const E& LIMIT,const bool MINIMUM = true)
{
LinkQueue<Edge<E>> ret;
if(asUndirected())
{
DynamicArray<int> adjVex(vCount());
DynamicArray<bool> mark(vCount());
DynamicArray<E> cost(vCount());
SharedPointer<Array<int>> aj = NULL;
bool end = false;
int v = 0;
for(int i = 0;i<vCount() ; i++)
{
adjVex[i] = -1;
mark[i] = false;
cost[i] = LIMIT;
}
mark[v] = true;
aj = getAdjacent(v);
for(int j = 0;j < aj->length();j++)
{
cost[(*aj)[j]] = getEdge(v,(*aj)[j]);
adjVex[(*aj)[j]] = v;
}
for(int i = 0;(i<vCount()) && !end;i++)
{
E m = LIMIT;
int k = -1;
for(int j = 0;j<vCount();j++)
{
if( !mark[j] && (MINIMUM ?(cost[j] < m) :(cost[j] > m)))
{
m = cost[j];
k = j;
}
}
end = (k == -1);
if(!end)
{
ret.add(Edge<E>(adjVex[k],k,getEdge(adjVex[k],k)));
mark[k] = true;
aj = getAdjacent(k);
for(int j = 0;j<aj->length();j++)
{
if(!mark[(*aj)[j]] && (MINIMUM ? (getEdge(k,(*aj)[j]) < cost[(*aj)[j]]):(getEdge(k,(*aj)[j]) > cost[(*aj)[j]])))
{
cost[(*aj)[j]] = getEdge(k,(*aj)[j]);
adjVex[(*aj)[j]] = k;
}
}
}
}
}
else
{
//抛出异常
}
if(ret.length() != (vCount() -1))
{
//抛出异常
}
return toArray(ret);
}
总结:
-最小生成树使得顶点间的连通代价最小
-Prim算法通过顶点的动态标记寻找最小生成树
-Prim算法的关键是集合概念的援用(T 集合,F 集合)
-利用Prim算法的思想也能寻找图的“最大生成树”