Dijkstra單源最短路徑

基本思想

  1. 假定一個(gè)源點(diǎn)u仁热,頂點(diǎn)集合V被劃分成兩個(gè)部分:集合 S 和集合 V-S 香缺。
  2. 初始時(shí)S僅包含源點(diǎn)u验游,S中的頂點(diǎn)到源點(diǎn)u的最短距離已經(jīng)確定吱雏,V-S中的頂點(diǎn)到源點(diǎn)u的最短距離待定。
  3. 用數(shù)組dist[]記錄每個(gè)頂點(diǎn)到源點(diǎn)u的最短距離筛峭。

算法流程

  1. 數(shù)據(jù)結(jié)構(gòu):設(shè)地圖的帶權(quán)鄰接矩陣為map[][]铐刘,如果源點(diǎn)u到頂點(diǎn)i有邊,則map[u][i]為<u,i>的權(quán)值影晓,否則map[u][i]為∞镰吵。利用一位數(shù)組dist[i]記錄頂點(diǎn)i到源點(diǎn)u的最短路徑。
  2. 初始化挂签, 令集合S = {u}疤祭,對(duì)于集合V-S中的所有頂點(diǎn)i,初始化dist[i]=map[u][i]饵婆。如果源點(diǎn)u與頂點(diǎn)i有邊相連勺馆,初始化p[i]=u,否則p[i]=-1,p[]用來記錄當(dāng)前頂點(diǎn)i的前驅(qū)節(jié)點(diǎn)谓传。
  3. 找最小蜈项,在集合V-S中依照貪心策略來尋找使得dist[j]具有最小值的頂點(diǎn)t,即dist[t]=min(dist[j] | j屬于V-S集合)续挟,那么頂點(diǎn)t就是此時(shí)V-S中距離源點(diǎn)u最近的頂點(diǎn)紧卒。
  4. 將t加入S集合, 同時(shí)更新V-S集合诗祸,也要更新與頂點(diǎn)t相連的其他頂點(diǎn)到源點(diǎn)u的距離跑芳。假設(shè)V-S集合中的頂點(diǎn)j與剛加入到S集合中的頂點(diǎn)t有邊權(quán)值為map[t][i],如果dist[j] > dist[t] + map[t][i]直颅,則dist[j] = dist[t] + map[t][i]博个,且更新頂點(diǎn)j的前驅(qū)p[j]=t,否則dist[j]保持不變功偿。
  5. 判斷集合S-V是否為空盆佣,若為空了,結(jié)束算法械荷,否則跳轉(zhuǎn)第3步共耍。

最終dist[]數(shù)組記錄了每個(gè)頂點(diǎn)到源點(diǎn)u的最短距離。
p[j]記錄了頂點(diǎn)j到源點(diǎn)u的最短路徑上的前驅(qū)節(jié)點(diǎn)吨瞎,通過p[]能找到頂點(diǎn)j到源點(diǎn)u最短路徑的路線痹兜。

一個(gè)簡(jiǎn)單的例子

#include<cstdio>
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int N = 100;//城市數(shù)量
const int INF = 1e7;//初始化無窮大值
int map[N][N], dist[N], p[N], n, m;//n為城市的個(gè)數(shù),m為城市間路線的條數(shù)
bool flag[N];//flag[i]為true颤诀,表明頂點(diǎn)i已加入到集合S
void Dijkstra(int u)
{
    for(int i=1; i<=n; i++)
    {
        dist[i] = map[u][i];//初始化源點(diǎn)u到各個(gè)頂點(diǎn)的距離
        flag[i] = false;
        if(dist[i] == INF)
            p[i] = -1;//源點(diǎn)u到該頂點(diǎn)的距離無窮大字旭,說明i點(diǎn)與源點(diǎn)不相鄰
        else
            p[i] = u;
    }
    dist[u] = 0;
    flag[u] = true;//初始化時(shí),集合S中只有一個(gè)元素崖叫,即源點(diǎn)u
    for(int i=1; i<=n; i++)
    {
        int temp = INF, t = u;
        for(int j=1; j<=n; j++)//在集合V-S中尋找距離源點(diǎn)u最近的頂點(diǎn)t
        {
            if(!flag[j]&&dist[j]<temp)
            {
                t = j;
                temp = dist[j];
            }
        }
        if(t==u) return;//找不到t遗淳,跳出循環(huán)
        flag[t] = true;//否則將t加入S集合
        for(int j=1; j<=n; j++)//更新集合V-S中與t相鄰的頂點(diǎn)到源點(diǎn)u的距離
        {
            if(!flag[j]&&map[t][j]<INF)
                if(dist[j]>(dist[t]+map[t][j]))
                {
                    dist[j] = dist[t] + map[t][j];
                    p[j] = t;
                }
        }
    }
}
int main()
{
    int u, v, w, st;
    cout << "請(qǐng)輸入城市的個(gè)數(shù):" << endl;
    cin >> n;
    cout << "請(qǐng)輸入城市之間路線的條數(shù):" << endl;
    cin >> m;
    cout << "請(qǐng)輸入城市之間的路線以及距離:" << endl;
    for(int i=1; i<=n; i++)//初始化鄰接矩陣
        for(int j=1; j<=n; j++)
            map[i][j] = INF;//初始化鄰接矩陣為無限大
    while(m--)
    {
        cin >> u >> v >> w;
        map[u][v] = min(map[u][v], w);//保留最小距離
    }
    cout << "請(qǐng)輸入當(dāng)前所在位置:" << endl;
    cin >> st;
    Dijkstra(st);
    cout << "起點(diǎn)所在位置:" << st << endl;
    for(int i=1; i<=n; i++)
    {
        cout << "起點(diǎn):" << st << " - " << "目的地:" << i << " ";
        if(dist[i] == INF)
            cout << "無法到達(dá)" << endl;
        else
            cout << "最短距離為:" << dist[i] << endl;
    }
    return 0; 
}

輸入和輸出

請(qǐng)輸入城市的個(gè)數(shù):
5
請(qǐng)輸入城市之間路線的條數(shù):
11
請(qǐng)輸入城市之間的路線以及距離:
1 5 12
5 1 8
1 2 16
2 1 29
5 2 32
2 4 13
4 2 27
1 3 15
3 1 21
3 4 7
4 3 19
請(qǐng)輸入當(dāng)前所在位置:
5
起點(diǎn)所在位置:5
起點(diǎn):5 - 目的地:1 最短距離為:8
起點(diǎn):5 - 目的地:2 最短距離為:24
起點(diǎn):5 - 目的地:3 最短距離為:23
起點(diǎn):5 - 目的地:4 最短距離為:30
起點(diǎn):5 - 目的地:5 最短距離為:0
  • 城市之間的距離是用有向圖來表示的,路徑表示都是單向的归露,如上面輸入的1 5 12洲脂,是指城市1到城市5有一條路長(zhǎng)度為12斤儿,不代表城市5到城市1有路可走剧包。
  • 若要表示無向圖,輸入1 5 12往果,默認(rèn)城市5到城市1也有一條長(zhǎng)度為12的路徑即可疆液。
  • 代碼中的p[]可以在有需要時(shí)用上,記錄的城市i的前驅(qū)節(jié)點(diǎn)陕贮,這樣可以依次找到從城市i到起點(diǎn)城市的逆向路徑堕油,使用棧逆序即可求出起點(diǎn)到各個(gè)頂點(diǎn)最短路徑的路線了。

算法分析

  1. 時(shí)間復(fù)雜度:O(n^2)
    • 最多只出現(xiàn)了兩重循環(huán)且長(zhǎng)度為n
  2. 空間復(fù)雜度:O(n)
    • 輔助空間包括一維數(shù)組flag[],i掉缺,j卜录,t,temp

算法優(yōu)化拓展

  • 使用優(yōu)先隊(duì)列優(yōu)化
#include<cstdio>
#include<iostream>
#include<cstring>
#include<stack>
#include<queue>
using namespace std;
const int N = 100;//城市數(shù)量
const int INF = 1e7;//初始化無窮大值
int map[N][N], dist[N], p[N], n, m;//n為城市的個(gè)數(shù)眶明,m為城市間路線的條數(shù)
bool flag[N];//flag[i]為true艰毒,表明頂點(diǎn)i已加入到集合S
struct Node
{
    int u,step;
    Node(){};
    Node(int a, int sp)
    {
        u = a;
        step = sp;
    }
    bool operator < (const Node& a)const //重載  <
    {
        return step > a.step;
    }
};
void Dijkstra(int u)
{
    priority_queue <Node> Q; //優(yōu)先隊(duì)列優(yōu)化
    Q.push(Node(u, 0));
    memset(flag, 0, sizeof(flag));//初始化flag數(shù)組為0
    for(int i=1; i<=n; i++)
        dist[i] = INF; //初始化所有距離為無限大
    dist[u] = 0;
    while(!Q.empty())
    {
        Node it = Q.top(); //優(yōu)先隊(duì)列對(duì)頭元素為最小值
        Q.pop();
        int t = it.u;
        if(flag[t]) //說明已經(jīng)找到了最短距離,該節(jié)點(diǎn)是隊(duì)列里面的重復(fù)元素
            continue;
        flag[t] = true;
        for(int i=1; i<=n; i++)
        {
            if(!flag[i]&&map[t][i]<INF)
            {
                if(dist[i]>dist[t]+map[t][i])
                {
                    //求距離當(dāng)前點(diǎn)的每個(gè)點(diǎn)的最短距離搜囱,進(jìn)行松弛操作
                    dist[i] = dist[t] + map[t][i];
                    Q.push(Node(i, dist[i]));//把更新后的最短距離壓入優(yōu)先隊(duì)列丑瞧,里面會(huì)有重復(fù)元素
                }
            }
        }
    }
}
int main()
{
    int u, v, w, st;
    cout << "請(qǐng)輸入城市的個(gè)數(shù):" << endl;
    cin >> n;
    cout << "請(qǐng)輸入城市之間路線的條數(shù):" << endl;
    cin >> m;
    cout << "請(qǐng)輸入城市之間的路線以及距離:" << endl;
    for(int i=1; i<=n; i++)//初始化鄰接矩陣
        for(int j=1; j<=n; j++)
            map[i][j] = INF;//初始化鄰接矩陣為無限大
    while(m--)
    {
        cin >> u >> v >> w;
        map[u][v] = min(map[u][v], w);//保留最小距離
    }
    cout << "請(qǐng)輸入當(dāng)前所在位置:" << endl;
    cin >> st;
    Dijkstra(st);
    cout << "起點(diǎn)所在位置:" << st << endl;
    for(int i=1; i<=n; i++)
    {
        cout << "起點(diǎn):" << st << " - " << "目的地:" << i << " ";
        if(dist[i] == INF)
            cout << "無法到達(dá)" << endl;
        else
            cout << "最短距離為:" << dist[i] << endl;
    }
    return 0; 
}
  1. 時(shí)間復(fù)雜度
    • while(!Q.empty()) 的執(zhí)行次數(shù)為n,因?yàn)橐獜棾鰊個(gè)最小值隊(duì)列才會(huì)為空
    • Q.pop()的時(shí)間復(fù)雜度為logn蜀肘,while語句中的for語句執(zhí)行n次绊汹,for語句中的Q.push()時(shí)間復(fù)雜度為logn
    • 因此,總的語句執(zhí)行次數(shù)為nlogn+(n^2)logn扮宠,算法的時(shí)間復(fù)雜度為O((n^2)logn)
  • 看似時(shí)間復(fù)雜度變大了西乖,是因?yàn)椴捎玫氖青徑泳仃嚕绻捎绵徑颖硖吃觯琭or語句的松弛操作就不用每次執(zhí)行n次了浴栽,而是邊的數(shù)量x,而每個(gè)節(jié)點(diǎn)的邊加起來為E轿偎,總的時(shí)間復(fù)雜度為O(nlogn+Elogn)典鸡,當(dāng)E>n時(shí),時(shí)間復(fù)雜度為O(E*logn)坏晦。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萝玷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昆婿,更是在濱河造成了極大的恐慌球碉,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仓蛆,死亡現(xiàn)場(chǎng)離奇詭異睁冬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)看疙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門豆拨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人能庆,你說我怎么就攤上這事施禾。” “怎么了搁胆?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵弥搞,是天一觀的道長(zhǎng)邮绿。 經(jīng)常有香客問我,道長(zhǎng)攀例,這世上最難降的妖魔是什么船逮? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮粤铭,結(jié)果婚禮上傻唾,老公的妹妹穿的比我還像新娘。我一直安慰自己承耿,他們只是感情好冠骄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著加袋,像睡著了一般凛辣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上职烧,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天扁誓,我揣著相機(jī)與錄音,去河邊找鬼蚀之。 笑死蝗敢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的足删。 我是一名探鬼主播寿谴,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼失受!你這毒婦竟也來了讶泰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤拂到,失蹤者是張志新(化名)和其女友劉穎痪署,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兄旬,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狼犯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了领铐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悯森。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖罐孝,靈堂內(nèi)的尸體忽然破棺而出呐馆,到底是詐尸還是另有隱情,我是刑警寧澤莲兢,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布汹来,位于F島的核電站,受9級(jí)特大地震影響改艇,放射性物質(zhì)發(fā)生泄漏收班。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一谒兄、第九天 我趴在偏房一處隱蔽的房頂上張望摔桦。 院中可真熱鬧,春花似錦承疲、人聲如沸邻耕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兄世。三九已至,卻和暖如春啊研,著一層夾襖步出監(jiān)牢的瞬間御滩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工党远, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留削解,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓沟娱,卻偏偏與公主長(zhǎng)得像氛驮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子济似,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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