堆優化的Dijkstra算法


(前面都是廢話)

下面是Dijkstra人物生平(摘自百度百科):

艾茲格·W·迪科斯徹 (Edsger Wybe Dijkstra,1930年5月11日~2002年8月6日)荷蘭人。 計算機科學家,畢業就職於荷蘭Leiden大學,早年鑽研物理數學,而后轉為計算學。曾在1972年獲得過素有計算機科學界的諾貝爾獎之稱的圖靈獎,之后,他還獲得過1974年 AFIPS Harry Goode Memorial Award、1989年ACM SIGCSE計算機科學教育教學傑出貢獻獎、以及2002年ACM PODC最具影響力論文獎。[1]

(說實話,這種東西看都不用看,要不是為了湊字數我也不會放上2333)

 

下面是算法講解部分

 

 

 

1、最短路問題(有基礎可以直接跳至小標題2)

所謂最短路問題,即在一個圖中,求一點到達任意一點的最短距離,如圖1

 

如圖,這是一個有向圖(圖中每條邊只能從其一個端點a走向另一個端點b,而不能從b走向a,相反地,無向圖中的邊既可以從a走向b也可以從b走向a)圖中的圓圈即我們通常說的“頂點”,兩個頂點之間的線就是我們所說的“”,圓圈中的數即這個頂點的編號,邊旁邊的數即“權值”(可以理解為邊的長度)


現在我們想求頂點1到頂點3的最短路(即使一個頂點到達另一個頂點所走的邊的權值之和最小),顯然有兩種走法:

一種是1->3,另一種是1->2->3

很容易看出,1->2->3這種走法所經過的邊的權值之和最小,即頂點1到頂點3的最短路

 

那么你可能會想了,要怎么使計算機像你一樣求出最短路呢?這就引出了本文的主題——Dijkstra算法

 

 

 

2、Dijkstra算法

 

算法簡介:

Dijkstra算法是由荷蘭神犇Dijkstra發明的最短路算法,是目前最快的尋路算法,堆優化后時間復雜度可達O((m+n)logm),但缺點是不能處理負權邊。

 

負權邊是什么:負權邊,即權值為負的邊,如果用邊是長度的比喻有點難理解,下面我用一下rqy神仙的比喻:

每一條邊的權值可以認為是從其一端a到另一端b的過路費,而負權就是有土豪在地上撒錢,不但沒有過路費還會讓你花的總錢數變少

是不是很生動形象(手動滑稽)

算法思想:

Dijkstra算法的思想就是每一次選擇一條未被選擇的且權值最小的邊連入最短路中,並用新連入的邊的去松弛(即假設有三點a,b,c,a到c有一條邊權值為10,但由a出發經過b再到c所經過的邊的權值之和為5,那么我們就把a到c的最短路值修改為5)所有其起始端頂點的子節點,其實就是一種貪心

 

顯然地,我們假設頂點數為n,邊數為m,則此算法的時間復雜度為O(n^2),這顯然是遠遠不夠的。於是,堆優化的Dijkstra算法誕生了

 

 

 

3、堆優化Dijkstra算法


堆優化Dijkstra算法,即運用堆排序來加快找邊的速度,使算法總體時間復雜度達到O((n+m)logm)

總的來說,就是把圖中所有邊都壓入一個最小堆(即根節點的值最小的堆)中,每次可以直接取堆頂元素入最短路,取邊的時間復雜度為O(1),但維護最小堆的時間復雜度為O(log m)

(下面是C++代碼實現,我用C++自帶的STL庫中的優先隊列模擬最小堆)

#include<bits/stdc++.h>
#define inf 0x7ffffff
using namespace std;
int n;
struct node{
    int x,dis;
    bool operator<(const node&a)const{
        return dis>a.dis;
    }
};
vector<int>son[2505],v[2505];
void make(int from,int to,int key){
    son[from].push_back(to);
    v[from].push_back(key);
}
#define to son[rt.x][i]
int dis[2505],tim[2505];
bool vis[2505];
priority_queue<node>q;
void dijkstra(int s){
    for(int i=1;i<=n;i++){
        dis[i]=inf;
    }
    dis[s]=0;
    q.push((node){s,0});
    while(!q.empty()){
        node rt=q.top();
        q.pop();
        if(!vis[rt.x]){
            vis[rt.x]=1;
            for(int i=0;i<son[rt.x].size();i++){
                if(dis[to]>dis[rt.x]+v[rt.x][i]){
                    dis[to]=dis[rt.x]+v[rt.x][i];
                    q.push((node){to,dis[to]});
                }
            }
        }
    }
}
int main(){
    int m,s,z;
    scanf("%d%d%d%d",&n,&m,&s,&z);
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        make(x,y,z);
        make(y,x,z);
    }
    dijkstra(s);
    printf("%d",dis[z]);
    return 0;
}

(C++神教萬歲!)

題目:洛谷P1339熱浪

 

 

 

4、斐波那契堆優化的Dijkstra

使用斐波那契堆可以使Dijkstra算法的時間復雜度進一步優化,這個坑以后再填。

(我才不會說是我還不會呢哼)

 

引用:

[1]百度百科 Dijkstra


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM