數(shù)據(jù)結(jié)構(gòu)——最短路徑Dijkstra算法

在上一篇博文里紊浩,我記錄了最小生成樹的算法實(shí)現(xiàn),而在這篇里疗锐,我們來講講查找最短路徑的算法坊谁,Dijkstra算法。

Dijkstra's algorithm常用于路由算法或者作為其他圖算法的一個(gè)子模塊滑臊。距離來說口芍,如果我們將圖的頂點(diǎn)理解為每個(gè)城市,而邊上的權(quán)重表示城市間開車行徑的路徑雇卷,該算法可以用來找到兩個(gè)城市之間的最短路徑鬓椭。

Dijkstra算法是通過為每個(gè)頂點(diǎn)v保留目前為止所找到的從s到v的最短路徑來工作的颠猴。初始時(shí),原點(diǎn)s的路徑權(quán)重被賦為0(d[s] = 0)小染。若對于頂點(diǎn)s存在能直接到達(dá)的邊翘瓮,則比較路徑的長度,如果路徑更短則更新存儲(chǔ)的值裤翩,當(dāng)算法結(jié)束時(shí)资盅,d[v]中存儲(chǔ)的便是從s到v的最短路徑,或者如果路徑不存在的話則是無法訪問踊赠,用marked數(shù)組來記錄從s到點(diǎn)v是否存在路徑呵扛。下面我們來看Dijkstra算法的代碼實(shí)現(xiàn),首先是C++版本:

#include <iostream>
#include <vector>
#include <stack>
#include "Edge.h"
#include "IndexMinHeap.h"

using namespace std;

// Dijkstra算法求最短路徑
template <typename Graph, typename Weight>
class Dijkstra {
private:
    Graph &G;                        // 圖的引用
    int s;                           // 起始點(diǎn)
    Weight *distTo;                  // distTo[i]存儲(chǔ)從起始點(diǎn)s到i的最短路徑長度
    bool *marked;                    // 標(biāo)記數(shù)組臼疫,在算法運(yùn)行過程中標(biāo)記節(jié)點(diǎn)i是否被訪問
    vector<Edge<Weight> *> from;     // from[i]記錄最短路徑中择份,到達(dá)i點(diǎn)的邊是哪一條
                                     // 可以用來恢復(fù)整個(gè)最短路徑

public:
    // 構(gòu)造函數(shù),使用Dijkstra算法求最短路徑
    Dijkstra(Graph &graph, int s):G(graph) {

        // 算法初始化
        assert( s >= 0 && s < G.V() );
        this->s = s;
        distTo = new Weight[G.V()];
        marked = new bool[G.V()];
        for (int i = 0; i < G.V(); i++) {
            distTo[i] = Weight();
            marked[i] = false;
            from.push_back(NULL);
        }

        // 使用索引堆記錄當(dāng)前找到的到達(dá)每個(gè)頂點(diǎn)的最短距離
        IndexMinHeap<Weight> ipq(G.V());

        // 對于起始點(diǎn)s進(jìn)行初始化
        distTo[s] = Weight();
        from[s] = new Edge<Weight>(s, s, 0);
        ipq.insert(s, distTo[s]);
        marked[s] = true;
        while( !ipq.isEmpty() ) {
            int v = ipq.extractMinIndex();
            // distTo[v] 就是s到v的最短距離
            marked[v] = true;
            // 對v的所有相鄰節(jié)點(diǎn)進(jìn)行更新
            typename Graph::adjIterator adj(G, v);
            for ( Edge<Weight> *e = adj.begin(); !adj.end(); e = adj.next() ) {
                int w = e->other(v);
                // 如果從s點(diǎn)到w點(diǎn)的最短路徑還沒有找到
                if ( !marked[w] ) {
                    // 如果w點(diǎn)以前沒有訪問過
                    // 或者訪問過烫堤,但是通過當(dāng)前的v點(diǎn)到w點(diǎn)距離更短荣赶,則進(jìn)行更新
                    if ( from[w] == NULL || distTo[v] + e->wt() < distTo[w] ) {
                        distTo[w] = distTo[v] + e->wt();
                        from[w] = e;
                        if ( ipq.contain(w) )
                            ipq.change( w, distTo[w] );
                        else
                            ipq.insert( w, distTo[w] );
                    }
                }
            }
        }
    }

    // 析構(gòu)函數(shù)
    ~Dijkstra() {
        delete[] distTo;
        delete[] marked;
        delete from[0];
    }

    // 返回從s點(diǎn)到w點(diǎn)的最短路徑長度
    Weight shortestPathTo( int w ) {
        assert( w >= 0 && w < G.V() );
        assert( hasPathTo(w) );
        return distTo[w];
    }

    // 判斷從s點(diǎn)到w點(diǎn)是否聯(lián)通
    bool hasPathTo( int w ) {
        assert( w >= 0 && w < G.V() );
        return marked[w];
    }

    // 尋找從s到w的最短路徑,將整個(gè)路徑經(jīng)過的邊存放在vec中
    void shortestPath( int w, vector< Edge<Weight> > &vec ) {
        assert( w >= 0 && w < G.V() );
        assert( hasPathTo(w) );

        // 通過from數(shù)組逆向查找到從s到w的路徑鸽斟,存放在棧中
        stack<Edge<Weight>*> s;
        Edge<Weight> *e = from[w];
        while(e->v() != this->s) {
            s.push(e);
            e = from[e->v()];
        }

        s.push(e);

        // 從棧中依次取出元素拔创,獲得順序的從s到w的路徑
        while( !s.empty() ) {
            e = s.top();
            vec.push_back(*e);
            s.pop();
        }
    }

    // 打印從s點(diǎn)到w點(diǎn)的路徑
    void showPath( int w ) {
        assert( w >= 0 && w < G.V() );
        assert( hasPathTo(w) );

        vector< Edge<Weight> > vec;
        shortestPath( w, vec );
        for (int i = 0; i < vec.size(); i++) {
            cout << vec[i].v() << " -> ";
            if ( i == vec.size() - 1 ) {
                cout << vec[i].w() << endl;
            }
        }
    }
};

之后是java版本的:

// Dijkstra算法求最短路徑
public class Dijkstra<Weight extends Number & Comparable> {

    private WeightedGraph G;      // 圖的引用
    private int s;                // 起始點(diǎn)
    private Number[] distTo;      // distTo[i]存儲(chǔ)從起始點(diǎn)s到點(diǎn)i的最短路徑長度
    private boolean[] marked;     // 標(biāo)記數(shù)組,在算法運(yùn)行過程中標(biāo)記節(jié)點(diǎn)是否被訪問
    private Edge<Weight>[] from;  // 可以用來恢復(fù)整個(gè)最短路徑

    // 構(gòu)造函數(shù)富蓄,使用Dijkstra算法求最短路徑
    Dijkstra(WeightedGraph graph, int s) {

        // 算法初始化
        this.G = graph;
        assert s >= 0 && s < G.V();
        this.s = s;
        distTo = new Number[G.V()];

        for (int i = 0; i < G.V(); i++) {
            distTo[i] = 0.0;
            marked[i] = false;
            from[i] = null;
        }

        // 使用索引堆記錄當(dāng)前找到的每個(gè)到達(dá)頂點(diǎn)的最短距離                                                                     iikkkkkk
        IndexMinHeap<Weight> ipq = new IndexMinHeap<Weight>(G.V());

        // 對于起始點(diǎn)s進(jìn)行初始化
        distTo[s] = 0.0;
        from[s] = new Edge<Weight>(s, s, (Weight)(Number)(0.0));
        ipq.insert(s, (Weight) distTo[s]);
        marked[s] = true;

        while (!ipq.isEmpty()) {
            int v = ipq.extractMinIndex();

            // distTo[v]就是s到v的最短距離
            marked[v] = true;

            // 對v的所有相鄰節(jié)點(diǎn)進(jìn)行更新
            for (Object item : G.adj(v)) {
                Edge<Weight> e = (Edge<Weight>) item;
                int w = e.other(v);

                // 如果s點(diǎn)到w點(diǎn)的最短路徑還沒有找到
                if (!marked[w]) {

                    // 如果w點(diǎn)以前沒有訪問過
                    // 或者訪問過剩燥,但是通過當(dāng)前v點(diǎn)到w點(diǎn)的距離g更短,則進(jìn)行更新
                    if (from[w] == null || distTo[v].doubleValue() + e.wt().doubleValue() < distTo[w].doubleValue()) {
                        distTo[w] = distTo[v].doubleValue() + e.wt().doubleValue();
                        from[w] = e;
                        if ( ipq.contain(w) ) {
                            ipq.change(w, (Weight) distTo[w]);
                        } else {
                            ipq.insert(w, (Weight) distTo[w]);
                        }
                    }
                }
            }
        }
    }

    // 返回從s點(diǎn)到w點(diǎn)的最短路徑長度
    public Number shortestPathTo(int w) {
        assert w >= 0 && w < G.V();
        assert hasPathTo(w);
        return distTo[w];
    }

    // 判斷從s點(diǎn)到w點(diǎn)是否聯(lián)通
    public boolean hasPathTo(int w) {
        assert w >= 0 && w < G.V();
        return marked[w];
    }

    // 尋找從s點(diǎn)到w點(diǎn)的最短路徑立倍,將整個(gè)路徑存放在vec中
    private Vector<Edge<Weight>> shortestPath(int w) {
        assert w >= 0 && w < G.V();
        assert hasPathTo(w);

        // 通過from數(shù)組逆向查找從s到w的路徑灭红,存放到棧中
        Stack<Edge<Weight>> s = new Stack<Edge<Weight>>();
        Edge<Weight> e = from[w];
        while (e.v() != this.s) {
            s.push(e);
            e = from[e.v()];
        }

        s.push(e);

        // 從棧中依次取出元素,獲得順序的從s到w的路徑
        Vector<Edge<Weight>> res = new Vector<Edge<Weight>>();
        while (!s.isEmpty()) {
            e = s.pop();
            res.add(e);
        }

        return res;
    }

    // 打印出從s點(diǎn)到w點(diǎn)的路徑
    public void showPath(int w){

        assert w >= 0 && w < G.V();
        assert hasPathTo(w);

        Vector<Edge<Weight>> path =  shortestPath(w);
        for( int i = 0 ; i < path.size() ; i ++ ){
            System.out.print( path.elementAt(i).v() + " -> ");
            if( i == path.size()-1 )
                System.out.println(path.elementAt(i).w());
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末口注,一起剝皮案震驚了整個(gè)濱河市变擒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寝志,老刑警劉巖娇斑,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異材部,居然都是意外死亡毫缆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門乐导,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苦丁,“玉大人,你說我怎么就攤上這事兽叮》医荆” “怎么了猾愿?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長账阻。 經(jīng)常有香客問我蒂秘,道長,這世上最難降的妖魔是什么淘太? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任姻僧,我火速辦了婚禮,結(jié)果婚禮上蒲牧,老公的妹妹穿的比我還像新娘撇贺。我一直安慰自己,他們只是感情好冰抢,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布松嘶。 她就那樣靜靜地躺著,像睡著了一般挎扰。 火紅的嫁衣襯著肌膚如雪翠订。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天遵倦,我揣著相機(jī)與錄音尽超,去河邊找鬼。 笑死梧躺,一個(gè)胖子當(dāng)著我的面吹牛似谁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掠哥,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼巩踏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了续搀?” 一聲冷哼從身側(cè)響起蛀缝,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎目代,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗤练,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡榛了,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年煞抬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霜大。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡革答,死狀恐怖战坤,靈堂內(nèi)的尸體忽然破棺而出曙强,到底是詐尸還是另有隱情,我是刑警寧澤途茫,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布碟嘴,位于F島的核電站,受9級特大地震影響囊卜,放射性物質(zhì)發(fā)生泄漏娜扇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一栅组、第九天 我趴在偏房一處隱蔽的房頂上張望雀瓢。 院中可真熱鬧,春花似錦玉掸、人聲如沸刃麸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泊业。三九已至,卻和暖如春断傲,著一層夾襖步出監(jiān)牢的瞬間脱吱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工认罩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留箱蝠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓垦垂,卻偏偏與公主長得像宦搬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子劫拗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容