贪心算法——单源最短路径(Dijkstra算法)

关于贪心算法介绍:
http://blog.csdn.net/mind_v/article/details/72956707

单源最短路径——Dijkstra算法

问题描述

对于给定的加权有向图G(加权邻接数组表示),它的每一条边(i,j)都有一个固定成本(权值)a[i][j],一条路径的长度就是该路径上所有边的的成本之和。如下图所示,路径124的长度是8,路径125的长度是9。
寻找一条从给定的一个源顶点出发到任意顶点的(目的顶点)的最短路径。如1到5的最短路径为1345。

贪心算法——单源最短路径(Dijkstra算法)_第1张图片

贪心法求解

根据贪婪法,每一步生成一条当前顶点的最短路径。每一条最短路径的目的顶点的选择方法依据贪婪准则:从一条最短路径还没有到达的顶点中,选择一个可以产生最短路径的目的顶点。

我们定义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)。

你可能感兴趣的:(数据结构与算法,贪心算法)