簡體   English   中英

Dijkstra算法的復雜度

[英]Complexity Of Dijkstra's algorithm

我從許多資料中獲悉,如果使用天真的方法來獲取min元素(線性搜索),Dijkstra的最短路徑也將以O(V ^ 2)復雜度運行。 但是,如果使用優先級隊列,則可以將其優化為O(VLogV),因為此數據結構將在O(1)時間返回min元素,但是在刪除min元素后需要O(LogV)時間來恢復堆屬性。

我在以下鏈接中針對UVA問題的以下代碼中實現了Dijkstra的算法: https ://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=16&page=show_problem&problem =1927

#include<iostream>
#include<vector>
#include <climits>
#include <cmath>
#include <set>

using namespace std;

#define rep(a,b,c) for(int c=a;c<b;c++)

typedef std::vector<int> VI;
typedef std::vector<VI> VVI;

struct cmp {
    bool operator()(const pair<int,int> &a,const pair<int,int> &b) const {
        return a.second < b.second;
    }
};

void sp(VVI &graph,set<pair<int,int>,cmp> &minv,VI &ans,int S,int T) {
    int e = -1;
    minv.insert(pair<int,int>(S,0));
    rep(0,graph.size() && !minv.empty() && minv.begin()->first != T,s) {
        e = minv.begin()->first;
        minv.erase(minv.begin());
        int nb = 0;
        rep(0,graph[e].size(),d) {
            nb = d;
            if(graph[e][d] != INT_MAX && ans[e] + graph[e][d] < ans[d]) {
                set<pair<int,int>,cmp>::iterator si = minv.find(pair<int,int>(d,ans[d]));
                if(si != minv.end())
                    minv.erase(*si);
                ans[d] = ans[e] + graph[e][d];
                minv.insert(pair<int,int>(d,ans[d]));
            }
        }
    }
}

int main(void) {
    int cc = 0,N = 0,M = 0,S = -1,T = -1,A=-1,B=-1,W=-1;
    VVI graph;
    VI ans;
    set<pair<int,int>,cmp> minv;
    cin >> cc;

    rep(0,cc,i) {
        cin >> N >> M >> S >> T;
        graph.clear();
        ans.clear();
        graph.assign(N,VI());
        ans.assign(graph.size(),INT_MAX);
        minv.clear();
        rep(0,N,j) {
            graph[j].assign(N,INT_MAX);
        }
        ans[S] = 0;
        graph[S][S] = 0;
        rep(0,M,j) {
            cin >> A >> B >> W;
            graph[A][B] = min(W,graph[A][B]);
            graph[B][A] = min(W,graph[B][A]);
        }
        sp(graph,minv,ans,S,T);
        cout << "Case #" << i + 1 << ": ";
        if(ans[T] != INT_MAX)
            cout << ans[T] << endl;
        else
            cout << "unreachable" << endl;
    }
}

根據我的分析,我的算法具有O(VLogV)復雜度。 STL std :: set被實現為二進制搜索樹。 此外,該集合也被排序。 因此,從中獲得的最小元素為O(1),插入和刪除的每個元素均為O(LogV)。 但是,我仍然可以從這個問題中獲得TLE,根據給定的時間限制,該問題應該可以在O(VLogV)中解決。

這使我思考得更深。 如果所有節點都互連在一起,以使每個頂點V具有V-1鄰居,該怎么辦? 因為每個頂點必須每輪都查看V-1,V-2,V-3 ...節點,這是否會使Dijkstra的算法在O(V ^ 2)中運行?

再三考慮,我可能會誤解最壞情況的復雜性。 有人可以在以下問題上給我建議:

  1. 鑒於上述反例,Dijkstra的算法O(VLogV)的表現如何?
  2. 如何優化代碼,使其達到O(VLogV)復雜度(或更高)?

編輯:

我意識到我的程序畢竟不能在O(ElogV)中運行。 瓶頸是由我在O(V ^ 2)中運行的輸入處理引起的。 dijkstra部分確實在(ElogV)中運行。

為了了解Dijkstra算法的時間復雜度,我們需要研究在用於實現Frontier集的數據結構(即算法中用於minv的數據結構)上執行的操作:

  • 插入
  • 更新資料
  • 查找/刪除最小值

在整個算法期間,數據結構上總共發生O(|V|)插入, O(|E|)更新, O(|V|)查找/刪除最小值。

  • 最初,Dijkstra使用未排序的數組實現了Frontier集。 因此,對於插入和更新,它為O(1) ,但是對於查找/刪除最小值,它為O(1) O(|V|) ,從而導致O(|E| + |V|^2) ,但是由於|E| < |V|^2 |E| < |V|^2 ,您有O(|V|^2)

  • 如果使用二進制min-heap來實現Frontier集,則所有操作都具有log(|v|) ,結果為O(|E|log|V| + |V|log|V|) ,但是由於假設|E| > |V|是合理的 |E| > |V| ,您有O(|E|log|V|)

  • 然后是Fibonacci堆,在這里您有O(1)最小插入/更新/查找時間,但有O(log|V|)最小刪除時間,為您提供了當前最廣為人知的O(|E| + |V|log|V|)表示Dijkstra的算法。

最后,如果(|V|log|V| < |E|)不能解決O(|V|log|V|)最壞情況下的時間復雜度的單源最短路徑問題的算法,因為該問題具有O(|E| + |V|)較小下限,即您需要檢查每個頂點和邊至少一次以解決問題。

使用BST或堆來改進Dijkstra會導致時間復雜度,例如O(|E|log|V|)O(|E|+|V|log|V|) ,請參見Dijkstra的運行時間 必須在某個點檢查每個邊緣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM