获取多条最短路径的Dijkstra算法

Dijkstra算法是单源最短路径经典算法,一般用于所有边的权为非负数的情况下,有向图和无向图均可。

效率方面:存储图模型的数据结构有很多种,使用邻接矩阵的话其空间复杂度都为O(E^2)。而如果是稀疏图,使用邻接链表更划算,空间复杂度为O(V+E)。在每次搜索离起点最近的点方面,这里用的还是vector(最近在复习vector….),所以每一次的时间复杂度还是O(N),其实可以使用优先队列实现,将时间复杂度降到O(logN)。

路径数目方面:一般算法使用一个一维数组记录每个点的前驱点,以此记录指定点到每个点的最短路径。但是由于每个点只保留一个前驱点,因此最后得到的最短路径只有一条,寻找过程中其他距离相等的最短路径会被抛弃,而做到保存多条最短路径,可以让每一个点都维护一个自己的前驱点数组。对于点i,在遇到距离相同的路径的时候,把i在这条路径的前驱点记录下来,而不是抛弃或者覆盖;而遇到更短的路径的时候,把i的前驱点数组清空,存入新的前驱点。这样最后每个点的前驱数组大小都不同。

    /**
     *
     * @param start 起点
     * @param dest 终点
     * @param adj 邻接链表。adj[i][k]表示节点i的第k个邻接点的索引
     * @param distance 到起点的距离。distance[i]表示起点到点i的距离
     * @return prevPoints 前驱点数组。 prevPoints[k]表示到达点k的最短路径中的所有前驱点
     */

    vector<vector<int> > dijkstra(vector<vector<int> > adj,int start, int dest=-2vector<int> distance) {
        unsigned long num = adj.size();
        vector<bool> visited(num, false);
        visited[start] = true;

        vector<vector<int> > prevPoints;

        //前驱点数组初始化
        for (int i = 0; i < num; ++i) {
            if (distance[i] < 999) {
                prevPoints.push_back(vector<int>(1,start));
            }
            else{
                prevPoints.push_back(vector<int>(1,-1));
            }
        }

        if (prevPoints[dest][0] == start) {
            return prevPoints;
        }

        for (int i = 1; i < num; ++i) {
            // 找离起点最近的点
            // 这里的复杂度还是O(N),可以通过使用优先队列优化
            int closest = 999;
            int toVisit = -1;
            for (int j = 0; j < num; ++j) {
                if (!visited[j] && distance[j] < closest) {
                    closest = distance[j];
                    toVisit = j;
                }
            }

            //如果是要找指定终点dest,可以提前剪枝,
            //但这样的话未访问的点的路径就不能保证是最短的。
            if (toVisit != -1 && !(dest != -2 && toVisit == dest) ) {
                //更新最短路径
                visited[toVisit] = true;
                for (int k = 0; k < adj[toVisit].size(); k++) {
                    if (adj[toVisit][k] != -1 && !visited[adj[toVisit][k]]) {
                        if (distance[adj[toVisit][k]] > distance[toVisit] + 1) {
                            //update the distance
                            distance[adj[toVisit][k]] = distance[toVisit] + 1;
                            //clear the paths,and store the new path
                            prevPoints[adj[toVisit][k]].clear();
                            prevPoints[adj[toVisit][k]].push_back(toVisit);

                        } else if (distance[adj[toVisit][k]] == distance[toVisit] + 1) {
                            //add the new path
                            prevPoints[adj[toVisit][k]].push_back(toVisit);
                        }
                    }
                }
            } else {
                break;
            }
        }
        return prevPoints;
    }

得到的前驱点数组可以用DFS从终点往回获取。

     if (prevPoints[dest][0] != -1){
            index = dest;
            results = getPaths(start,index,prevPoints);
        }
   /**
     *
     * @param start 起点
     * @param index 终点
     * @param prevPoints 前驱点数组。prevPoints[k]表示到达点k的最短路径中的所有前驱点
     * @return paths 路径。paths[k]表示从start到index的第k条最短路径
     */
    vector<vector<int> > getPaths(int start,int index, vector<vector<int> >& prevPoints){
        vector<vector<int> >childPaths;
        vector<vector<int> >midPaths;
        if (index != start){
            for (int i = 0; i < prevPoints[index].size(); ++i) {
                childPaths = getPaths(start,prevPoints[index][i],prevPoints);
                for (int j = 0; j < childPaths.size(); ++j) {
                    childPaths[j].push_back(index);
                }
                if(midPaths.empty()){
                    midPaths = childPaths;
                } else{
                   midPaths.insert(midPaths.end(),childPaths.begin(),childPaths.end());
                }
            }
        }
        else{
            // 第一个点
            midPaths.push_back(vector<int>(1,start));
        }
        return midPaths;
    }

你可能感兴趣的:(基础算法学习笔记,c语言,dijkstra,算法)