国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解

這篇具有很好參考價值的文章主要介紹了【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解

  • 博主簡介:努力學(xué)習(xí)的大一在校計算機專業(yè)學(xué)生,熱愛學(xué)習(xí)和創(chuàng)作。目前在學(xué)習(xí)和分享:算法、數(shù)據(jù)結(jié)構(gòu)、Java等相關(guān)知識。
  • 博主主頁: @是瑤瑤子啦
  • 所屬專欄: 算法 ;該專欄專注于藍(lán)橋杯和ACM等算法競賽??
  • 近期目標(biāo):寫好專欄的每一篇文章

【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解

一、簡介

Dijkstra算法適用于最短路問題中,單源最短路(只有一個起點),并且每條邊的權(quán)重都是正數(shù)的情況

二、基本思想策略

首先假定源點為u(就是起點),頂點集合V被劃分為兩部分:集合 S 和 V-S。 初始時S中僅含有源點u,其中S中的頂點到源點的最短路徑已經(jīng)確定。
集合S 和V-S中所包含的頂點到源點的最短路徑的長度待定,稱從源點出發(fā)只經(jīng)過S中的點到達V-S中的點的路徑為特殊路徑(不一定是最短的)
并用dist[]記錄當(dāng)前每個頂點對應(yīng)的最短特殊路徑長度。

紅色頂點是S集合中,均已經(jīng)確定最短路徑的點,而綠色的是V-S集合中沒有確定該頂點到源點最短路徑的點。我們可以看到只經(jīng)過S中的點到綠色點有兩條特殊路徑:15+2020+10,但是只有一條最短路徑:15+20,那么綠色點就當(dāng)前情況來看,可以暫時把它的dist[i]更新為25,但是一定是最短特殊路徑嗎?不一定,為什么呢?我們接下來往下看

【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解
可以看到,V-S集合中假設(shè)存在一點T,經(jīng)過這點到目的點的距離,很有可能是目的點真正的dist!
【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解

可能這里可能有點暈,沒錯。上面只是一個大概的介紹,我們接下來徹底揭開它的神秘面紗。

選擇特殊路徑長度最短的路徑,將其連接的V-S中的頂點加入到集合S中,同時更新數(shù)組dist[](核心)。一旦S包含了所有頂點,dist[]就是從源到所有其他頂點的最短路徑長度

數(shù)據(jù)結(jié)構(gòu): h[N],e[M],ne[M],w[M]構(gòu)建帶權(quán)重的鄰接表,來存儲圖;int dist[N];//dist 數(shù)組保存源點到其余各個節(jié)點的距離

(1)初始化。令集合S={0},對于集合V-S中的所有頂點x{1,2,3…n};初始化dist數(shù)組memset(dist, 0x3f, sizeof(dist));//dist 數(shù)組的各個元素為無窮大,

(2)找最小在集合V-S中依照貪心策略來尋找使得dist[j]具有最小值的頂點t,即dist[t]=min,則頂點t就是集合V-S中距離源點u最近的頂點。
(3)加入S戰(zhàn)隊。將頂點t加入集合S,同時更新V-S
(4)借東風(fēng)。在(2)中已近找到了源點到t的最短路徑,那么對集合V-S中所有與頂點t相鄰的頂點j,都可以借助t走捷徑。
如果dist[i] = min(dist[i], dist[t] + w[j]);//更新 dist[j],轉(zhuǎn)(2)。

光憑這個好像是這么回事,但是細(xì)節(jié)值得推敲。這個題的本質(zhì)還是貪心。
比如只看剛剛的文字,不仔細(xì)分析,你能不能解決我開頭提出的那個問題?
為什么這么說呢。因為在目前(局部情況)來看,確實找到最短特殊路徑了,竟然就直接加入S戰(zhàn)隊,不太可取,就像我開頭提出的,在V-S集合中,萬一存在一個點,經(jīng)過這個點再到目的點,很有可能才是真正的最短距離。
那為什么這個算法是可取的呢?
巧就巧在,最外層循環(huán),遍歷了整個頂點,并且并不是說,一旦該頂點加入了S戰(zhàn)隊,它的dist就不能變了,相反,它在實時更新!。當(dāng)循環(huán)遍歷到綠色頂點T時,會更新與它相連節(jié)點的dist。
不知道有沒有g(shù)et到我的意思,雖然我沒有用公式啥的去推導(dǎo),我個人也非常討厭那種不人性化的方式,更喜歡用一種形象的,意會的方式去理解。

綜上,由局部最優(yōu),到全局最優(yōu),這種貪心的策略,完全可以保證全部遍歷和循環(huán)后,dist[]數(shù)組中存的就是該點到源點的最短距離?。。。ㄉ厦媸俏覀€人的理解,歡迎一起討論交流和學(xué)習(xí)捏?。?/p>

三、代碼實現(xiàn)

這里是AcWing 849.Dijkstra求最短路 I模板題目

給定一個 n 個點 m 條邊的有向圖,圖中可能存在重邊和自環(huán),所有邊權(quán)均為正值。

請你求出 1 號點到 n 號點的最短距離,如果無法從 1 號點走到 n 號點,則輸出 ?1。

輸入格式

第一行包含整數(shù) n 和 m。

接下來 m 行每行包含三個整數(shù) x,y,z,表示存在一條從點 x 到點 y 的有向邊,邊長為 z。

輸出格式

輸出一個整數(shù),表示 1 號點到 n 號點的最短距離。

如果路徑不存在,則輸出 ?1。

數(shù)據(jù)范圍

1≤n≤500,
1≤m≤105,
圖中涉及邊長均不超過10000。

3.1偽代碼詳解

int dist[N],state[N];
dist[1] = 0,state = 1;//把源點加入S集合
for (i : 1~n)
{
  1),t<-找最小,找到只經(jīng)過S中的點到V-S集合中某一點,距離最小的那個V-S中的那個點
  2),state[t] = 1;將t加入到S戰(zhàn)隊
   3),更新與t頂點相鄰點的dist
  
}

3.2源代碼詳解

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

//N是頂點數(shù),M是邊數(shù)
const int N =510,M = 100010;
int h[N],e[M],ne[M],w[M],idx;//鄰接表存儲圖,w[M]存儲邊的權(quán)重
int state[N];//state記錄是否找到了源點到該節(jié)點的最短距離
int dist[N];//dist保存源點到其余各個頂點的最短特殊距離
int n,m;//圖的頂點數(shù)和邊數(shù)

void add(int a,int b,int c)//插入邊,并給每個邊賦值權(quán)重
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a],h[a] = idx++;
}
//key:核心和關(guān)鍵部分!??!
void Dijkstra()
{
    memset(dist,0x3f,sizeof(dist));// 1)初始化dist數(shù)組
    
    dist[1] = 0;//源點到源點的距離當(dāng)然是0
    
    for (int i = 0; i < n; i++)//對n個頂點進行n次遍歷(一開始V-S集合為n個元素,S集合是0個,肯定是遍歷n次,才能完全遍歷完
    {
        int t = -1;//t存儲當(dāng)前這次遍歷到的V-S集合中的點,該點當(dāng)前局部情況距離源點距離最小的那個點
        //2)在V-S中 找最小
        for (int j = 1; j <= n; j++){
            if(!state[j] && (t == -1 || dist[j] < dist[t]))
                t = j;
        }
        state[t] = 1;//3)加入S 戰(zhàn)隊
        
        //3)借東風(fēng)
        for (int j = h[t];j != -1; j = ne[j])
        {
            int i = e[j];
            dist[i] = min(dist[i],dist[t]+w[j]);//更新dist[j]
        }                                                    
    }
}

int main(){
    memset (h,-1,sizeof (h));//初始化鄰接表
    
    cin >> n >>m;
    while (m--)//讀入m條邊
    {
        int a,b,w;
        cin >> a >> b >> w;
        add (a,b,w);
    }
    Dijkstra();
    if (dist[n] != 0x3f3f3f3f)//如果dist[n]被更新了,那么一定存在從1號節(jié)點到n號節(jié)點的最短距離路徑
        cout << dist[n];
    else
        cout << "-1";
}

關(guān)于存儲圖的適合,沒有考慮重邊和自環(huán)的影響?

因為在第三步更新的時候,即使鄰接表那條單鏈上有兩個一樣編號的節(jié)點,但是第三步更新的時候,還是會讓對應(yīng)編號節(jié)點的dist為最小。所以即使有重邊也不影響

3.4:數(shù)據(jù)結(jié)構(gòu)優(yōu)化

上面我們是采用鄰接表來存儲圖,鄰接表的原理如下。鄰接表是適合稀疏圖,當(dāng)邊比較多,也就是稠密圖時,我們采用鄰接矩陣來存儲圖。即g[a][b]的值為編號為a的節(jié)點a到編號為b的節(jié)點b之間的距離。

【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解
【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解

使用鄰接矩陣,注意去重邊,因為鄰接矩陣只允許a→b的距離唯一。

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 510;

int n,m;

int g[N][N];//鄰接矩陣存儲圖
int dist[N];
bool st[N];

int dijkstra(){
    memset(dist,0x3f,sizeof(dist)); 
    
    dist[1] = 0;
    
    for(int i = 0; i < n; i++){
        int t = -1;
        for (int j = 1; j <= n; j ++)
            if (!st[j] && (t == -1|| dist[t] > dist[j]))
                t = j;
        
        st[t] = true;
        
        for (int j = 1; j <= n; j++)
            dist[j] = min(dist[j],dist[t] + g[t][j]);
        
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main(){
    scanf("%d%d",&n,&m);
    memset(g,0x3f,sizeof g);//初始化鄰接矩陣
    
    while(m--){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        g[a][b] = min(g[a][b],c);//去除重邊
    }
    int t = dijkstra();
    
    printf("%d\n",t);
    
    return 0;
}

3.3:算法分析

算法時間復(fù)雜度:時間復(fù)雜是 O(n2+m)O(n2+m), n 表示點數(shù),m表示邊數(shù)

耗時的主要地方在于第2)步,找最小,每次都需要遍歷一遍dist數(shù)組,完全沒有必要??梢允褂?strong>小根堆來優(yōu)化(小根堆的數(shù)據(jù)結(jié)構(gòu)可以自己來實現(xiàn)(推薦),或者用庫中的)

四、使用小根堆來優(yōu)化Dijkstra算法

這個定義的heap,完全可以看作集合V-S的具體化!通過這個小根堆,可以直接取出(取出+刪除)V-S集合的最小值。

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>//堆的頭文件

using namespace std;

typedef pair<int, int> PII;//堆里存儲距離和節(jié)點編號

const int N = 1e6 + 10;

int n, m;//節(jié)點數(shù)量和邊數(shù)
int h[N], w[N], e[N], ne[N], idx;//鄰接表存儲圖
int dist[N];//存儲距離
bool st[N];//存儲狀態(tài)

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);//距離初始化為無窮大
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;//小根堆
    heap.push({0, 1});//插入距離和節(jié)點編號

    while (heap.size())
    {
        auto t = heap.top();//取距離源點最近的點
        heap.pop();

        int ver = t.second, distance = t.first;//ver:節(jié)點編號,distance:源點距離ver 的距離

        if (st[ver]) continue;//如果距離已經(jīng)確定,則跳過該點
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])//更新ver所指向的節(jié)點距離
        {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j], j});//距離變小,則入堆
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }

    cout << dijkstra() << endl;

    return 0;
}

使用小根堆后,找到 t 的耗時從 O(n^2) 將為了 O(1)。每次更新 dist 后,需要向堆中插入更新的信息。所以更新dist的時間復(fù)雜度有 O(e) 變?yōu)榱?O(elogn)。總時間復(fù)雜度有 O(n^2) 變?yōu)榱?O(n + elongn)。適用于稀疏圖。

五、深入和反思

最開始我們說到,Dijkstra算法只適用于邊的權(quán)重都是正數(shù)的情況。為什么負(fù)權(quán)邊不行呢?

看一個Dijkstra算法失效的例子:

【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解
A→B→D→E,確定dist[E]=20,dist[D]=8

然后A→C→D雖然更新了D點的dist,使之正確dist[D]=-1,但是由于D已經(jīng)被遍歷過,無法通過D來更新E,導(dǎo)致最終求出的A→E的最小距離出錯。

為什么呢?

D的dist的正確性不受負(fù)權(quán)的影響,是因為負(fù)權(quán)指向的是D,在更新節(jié)點,更新dist的時候,會更新掉D的錯誤值。但E就不一樣了,在當(dāng)前局部,只有D一個經(jīng)過它,D一旦遍歷過后,更新了E。當(dāng)經(jīng)過C到D時,無法再通過正確的D去更新E!

如果全部是正值的話,在A→D時,能一下子確定當(dāng)前真正的dist[D]!再dist[D]+12,那dist[E]也是正確的!

所以根本原因在于,存在負(fù)權(quán)邊,dist[D]的真值不能在更新dist[E]之前確定。

最后是我個人總結(jié)的理解:

??在Dijkstra算法視角,把B遍歷并進行相關(guān)更新后,它當(dāng)前得知了如下情況:dist[A] = 0,dist[B] = 2,dist[D] = 5,dist[C] = 999,dist[D] = 999+C,C>0,Dijkstra當(dāng)然會放棄A-C-D這條路,可是C其實<0,這條路不該被放棄,反而A-C-D的路徑長度很有可能會小于A-B-C的長度,正是由于Dijkstra的這點輸入,導(dǎo)致出現(xiàn)負(fù)權(quán)邊時,結(jié)果不正確,再說,人家的正確性本來就是建立在所有邊的權(quán)重都>0的基礎(chǔ)上!


【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解文章來源地址http://www.zghlxwxcb.cn/news/detail-401475.html

  • Java島冒險記【從小白到大佬之路】
  • LeetCode每日一題–進擊大廠
  • 算法

到了這里,關(guān)于【最短路算法】一篇文章徹底弄懂Dijkstra算法|多圖解+代碼詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • deque用法深度解析,一篇文章弄懂deque容器各種操作

    deque用法深度解析,一篇文章弄懂deque容器各種操作

    ?? 博客主頁:在下馬農(nóng)的碎碎念 ? 本文由在下馬農(nóng)原創(chuàng),首發(fā)于CSDN ?? 首發(fā)時間:2022/01/11 ?? 最近更新時間:2022/01/11 ?? 此馬非凡馬,房星本是星。向前敲瘦骨,猶自帶銅聲。 ?? 系列文章目錄: 暫無 ??作者水平有限,如發(fā)現(xiàn)錯誤,請留言轟炸哦!萬分感謝! ??碼字不

    2023年04月24日
    瀏覽(22)
  • 一篇文章弄懂Oracle和PostgreSQL的Database Link

    ?? 文章目標(biāo):本篇介紹Oracle和PostgreSQL的Database Link ?? 一篇文章弄懂Oracle和PostgreSQL的Database Link ? 創(chuàng)作者:Jay… ?? 個人主頁:Jay的個人主頁 ?? 展望:若本篇講解內(nèi)容幫助到您,請幫忙點個贊吧,再點點您的小手關(guān)注下,您的支持是我繼續(xù)寫作的最大動力,謝謝?? 作為回

    2024年02月06日
    瀏覽(20)
  • FPGA入門系列(1)產(chǎn)品選型(一篇文章弄懂FPGA不同系列)

    FPGA入門系列(1)產(chǎn)品選型(一篇文章弄懂FPGA不同系列)

    Xilin的FPGA芯片可以分為多個系列,我們常見的7系列、UltraScale、UltraScale+。 根據(jù)不同的工藝可以劃分為45nm、28nm、20nm、16nm,可以劃分為: 特別的在這些系列之外,還有ZYQN。這個系列主要的特點是在具備FPGA功能外,還額外封裝了一個處理器,比較的常見的是ZYNQ-7000系列,便宜

    2024年04月14日
    瀏覽(19)
  • 一篇文章讓你弄懂分布式一致性協(xié)議Paxos

    Paxos算法由Leslie Lamport在1990年提出,它是少數(shù)在工程實踐中被證實的強一致性、高可用、去中心的分布式協(xié)議。Paxos協(xié)議用于在多個副本之間在有限時間內(nèi)對某個決議達成共識。Paxos協(xié)議運行在允許消息重復(fù)、丟失、延遲或亂序,但沒有拜占庭式錯誤的網(wǎng)絡(luò)環(huán)境中,它利用“大

    2024年02月09日
    瀏覽(19)
  • 一篇文章徹底清楚shellcode(精品)

    一篇文章徹底清楚shellcode(精品)

    1.沒開沙箱(此時我們可以系統(tǒng)調(diào)用get shell) (一)32位程序系統(tǒng)調(diào)用 32位程序有別于64位程序,32位通過棧傳參,我們常用的寄存器有4個數(shù)據(jù)寄存器(eax,ebx,ecx,edx),2個變址寄存器(esi,edi),2個指針寄存器(esp,ebp). 下邊我們就來看一種系統(tǒng)調(diào)用方式及其構(gòu)造: 執(zhí)行上述shellcode即可g

    2024年02月09日
    瀏覽(19)
  • 數(shù)據(jù)結(jié)構(gòu)與算法之美學(xué)習(xí)筆記:41 | 動態(tài)規(guī)劃理論:一篇文章帶你徹底搞懂最優(yōu)子結(jié)構(gòu)、無后效性和重復(fù)子問題

    數(shù)據(jù)結(jié)構(gòu)與算法之美學(xué)習(xí)筆記:41 | 動態(tài)規(guī)劃理論:一篇文章帶你徹底搞懂最優(yōu)子結(jié)構(gòu)、無后效性和重復(fù)子問題

    本節(jié)課程思維導(dǎo)圖: 今天,我主要講動態(tài)規(guī)劃的一些理論知識。學(xué)完這節(jié)內(nèi)容,可以幫你解決這樣幾個問題:什么樣的問題可以用動態(tài)規(guī)劃解決?解決動態(tài)規(guī)劃問題的一般思考過程是什么樣的?貪心、分治、回溯、動態(tài)規(guī)劃這四種算法思想又有什么區(qū)別和聯(lián)系? 什么樣的問

    2024年02月02日
    瀏覽(38)
  • 一篇文章,帶你徹底掌握接口測試!

    一、什么是接口測試? 所謂接口,是指同一個系統(tǒng)中模塊與模塊間的數(shù)據(jù)傳遞接口、前后端交互、跨系統(tǒng)跨平臺跨數(shù)據(jù)庫的對接。而接口測試,則是通過接口的不同情況下的輸入,去對比輸出,看看是否滿足接口規(guī)范所規(guī)定的功能、安全以及性能方面的要求。 二、為什么要

    2024年02月10日
    瀏覽(34)
  • 一篇文章徹底理解自定義View

    目錄 一.View的基礎(chǔ)? 1.view的基礎(chǔ)概念 2.view的位置和事件event幾種表示法 3.view的滑動

    2024年02月01日
    瀏覽(22)
  • 讀這篇文章讓你徹底了解Redis

    讀這篇文章讓你徹底了解Redis

    你好,我是Redis,一個叫Antirez的男人把我?guī)У搅诉@個世界上。 說起我的誕生,跟關(guān)系數(shù)據(jù)庫MySQL還挺有淵源的。 在我還沒來到這個世界上的時候,MySQL過的很辛苦,互聯(lián)網(wǎng)發(fā)展的越來越快,它容納的數(shù)據(jù)也越來越多,用戶請求也隨之暴漲,而每一個用戶請求都變成了對它的一

    2024年02月04日
    瀏覽(28)
  • Unity/C#------委托與事件(一篇文章徹底搞懂...)

    Unity/C#------委托與事件(一篇文章徹底搞懂...)

    ? ? ? ? 所有的代碼語言創(chuàng)造者母語都是英語,我們從英語翻譯到中文的過程中難免會存在一些不太能還原本意的詞,比如我之前一直不理解構(gòu)造函數(shù)和析構(gòu)函數(shù),只知道這倆貨作用相反,直到我看到了它的英文意思,Construstor/Distructor,我才徹底理解了他們的作用。 ? ? ?

    2024年02月06日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包