关于贪心算法介绍:
http://blog.csdn.net/mind_v/article/details/72956707
单源最短路径——Dijkstra算法
问题描述
对于给定的加权有向图G(加权邻接数组表示),它的每一条边(i,j)都有一个固定成本(权值)a[i][j],一条路径的长度就是该路径上所有边的的成本之和。如下图所示,路径124的长度是8,路径125的长度是9。
寻找一条从给定的一个源顶点出发到任意顶点的(目的顶点)的最短路径。如1到5的最短路径为1345。
贪心法求解
根据贪婪法,每一步生成一条当前顶点的最短路径。每一条最短路径的目的顶点的选择方法依据贪婪准则:从一条最短路径还没有到达的顶点中,选择一个可以产生最短路径的目的顶点。
我们定义distanceFromSource[i]是在已生成的最短路径上再拓展一条最短边而到达顶点i时的最短路径长度。最开始,只有一条从sourceVertex到sourceVertex的路径,长度为0。这是对于每一个顶点,distanceFromSource[i]等于a[sourceVertex][i]。为产生下一条路径,需要选择一个还没最短路径到达的顶点,在这些顶点中,使distanceFromSource[]值最小的顶点就是下一条路径的终点。当得到一条最短路径之后,有些顶点的的distanceFromSource[]值可能会改变,因为由新的最短路径拓展可能得到更小的值。
算法步骤:
1)初始化distanceFromSource[i]=a[sourceVertex][i]
(1<= i <= n)
对所有邻接于sourceVertex
的顶点i,令prodecesoor[i]=sourceVertex
对所有的其他顶点,predecessor[sourceVertex]=0,predecessor[i]=-1
创建一个newReachableVertices
,存储所有predecessor[i]>0
的顶点(即这个表包含所有邻接于sourceVertex
的顶点)。
2)如果newReachableVertices
为空,算法终止,否则转置3)
3)从newReachableVertices
中删除distanceFromSource
值最小的i
4)对所有邻接于i的顶点j,将distanceFromSource[j]
更新min{distanceFromSource[j],distanceFromSource[i]+a[i][j]}。如果
distanceFromSource[j]改变,令
predecessor[j]=i,而且若j没有在
newReachableVertices`中,则将其加入进去。
注:newReachableVertices是一个链表对象,使用到的接口包括:
insert(index, element):在指定索引位置插入元素element的节点;
eraseElement(element):删除指定的元素节点;
begin():返回第一个元素的迭代器
end():返回最后一个元素后一个位置的迭代器
C++实现:
void shortestPaths(int sourceVertex, int *distanceFromSource, int numberOfVertices)
{//寻找从源顶点开始的最短路径
//从数组distanceFromSource中返回最短路径
//在数组predecessor中返回顶点在路径上的前驱的信息
if (sourceVertex < 1 || sourceVertex > n)
throw illegalParameterValue("Invalid source Vertex");
//确定是加权图
if (!weighted())
return;
List<int> newReachableVertices;
for (int i = 1; i <= n; ++i)
{
distanceFromSource[i] = a[sourceVertex][i];
if (distanceFromSource[i] == noEdge)
distanceFromSource[i] = -1;
else
{
predecessor[i] = sourceVertex;
newReachableVertices.insert(0, i);
}
}
distanceFromSource[sourceVertex] = 0;
Predecessor[sourceVertex] = 0;
//更新distanceFromSource和predecessor
while (!newReachableVertices.empty())
{//还存在更多的路径
//寻找distanceFromSource值最小的,还未达到的顶点v
List<int>::iterator iNewReachableVertices = newReachableVertices.begin();
List<int>::iterator end = newReachableVertices.end();
int v = *iNewReachableVertices;
iNewReachableVertices++;
while (iNewReachableVertices != end)
{
int w = *iNewReachableVertices;
iNewReachableVertices++;
if (distanceFromSource[w] < distanceFromSource[v])
v = w;
}
//下一条最短路径到达v
//先从newReachableVertices中删除v,然后更新distanceFromSource
for (int j = 1; j <= n; ++j)
{
if (a[v][j] != noEdge && (predecessor[j] == -1 ||
distanceFromSource[j] > distanceFromSource[v] + a[v][j]))
{
//distanceFromSource[j]减少
distanceFromSource[j] = distanceFromSource[v] + a[v][j];
if (predecessor[j] == -1)
//以前未到达,加入j
newReachableVertices.insert(0, j);
predecessor[j] = v;
}
}
}
}
使用邻接矩阵描述图时,第一个while循环的复杂度为O(n),第二个while循环和for循环都要达到所有的顶点,故也为O(n),所以整个算法的时间复杂度为O(n2)。