Dijkstra算法(求单源最短路径)

Dijkstra:

和Bellman-Ford算法都是求单源最短路径的,但Dijkstra算法要求图中没有负权边。

整体思路:集合S包含所有已经找到最短路径的节点,Q集合为没有找到最短路径的节点。

起初Q为V(原始顶点集),S为空集。每次从Q中选取节点v,v满足条件:s到v的路径权和最小。将v从Q中删除,加入S。

循环|V|次直到Q为空集,算法退出。

优化方式:

每次从Q中选取节点v需要O(N)时间。如果Q设计为优先队列,每次选取节点v只需要O(logN)时间。

struct EdgeNode{    //邻接链表节点
    int num;
    int weigh;
    EdgeNode* next;
    EdgeNode():num(0),weigh(0),next(nullptr){}
    EdgeNode(int x,int w):num(x),weigh(w),next(nullptr){}
};

struct vertex
{
    int num;
    int dis;
    vertex(int x):num(x),dis(10000){}
    vertex():num(0),dis(10000){}
};

struct cmp
{
    bool operator ()(const vertex& x1,const vertex& x2){
        return x1.dis>x2.dis;
    }
};


//Dijkstra算法
void Dijkstra(vector<EdgeNode*>& vertexList,int s){ //起点s,邻接链表vertexList
    int vertexNum=vertexList.size();
    priority_queue<vertex,vector<vertex>,cmp> p;    //小顶堆,dis最小的节点排在队首
    unordered_set<int> visited;//visited:已找到最短路径的顶点集合
    vector<int> dis(vertexNum,10000);//dis[i]:s到i的最短路径权和
    vector<int> pre(vertexNum,-1);  //pre[i]:s到i的最短路径上i的前继节点
    dis[s]=0;
    for(int i=0;i<vertexNum;++i){
        if(i==s){
            auto tmp=vertex(s);
            tmp.dis=0;
            p.push(tmp);
        }
        else{
            p.push(vertex(i));
        }
    }
    while(visited.size()<vertexNum){
        auto u=p.top();
        int uu=u.num;   //uu为当前未找到最短路径的节点中dis最小的,将它加入集合
        p.pop();
        if(visited.count(uu)){
            continue;
        }
        visited.insert(uu);
        auto node=vertexList[uu];
        while(node!=nullptr){
            int v=node->num;
            if(dis[v]>dis[uu]+node->weigh){     //用u-v边来松弛s-v的路径
                dis[v]=dis[uu]+node->weigh;
                pre[v]=uu;
                auto tmp=vertex(v);
                tmp.dis=dis[v];
                p.push(tmp);
            }
            node=node->next;
        }
    }
}

代码未验证,仅供参考!

posted @ 2020-06-03 00:17  NeoZy  阅读(266)  评论(0编辑  收藏  举报