2016-08-04 123 views
3

我從很多來源讀到,如果使用簡單的方法來獲得最小元素(線性搜索),Dijkstra的最短路徑也將以O(V^2)複雜度運行。但是,如果使用優先級隊列,則可優化爲O(VLogV),因爲此數據結構將在O(1)時間內返回最小元素,但在刪除最小元素後需要O(LogV)時間來恢復堆屬性。Dijkstra算法的複雜性

我已經實現的Dijkstra算法中在此鏈接中的UVA問題下面的代碼: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)中運行。

回答

4

爲了瞭解Dijkstra算法的時間複雜度,我們需要研究那些對用於實現邊境集的數據結構進行的操作(即在你的算法用於minv的數據結構):

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

O(|V|)插入,O(|E|)更新,O(|V|)查找/刪除算法整個持續時間內數據結構上發生的最小總數。

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

  • 如果二元最小堆用來實現邊境集,你有log(|v|)所有的操作,導致O(|E|log|V| + |V|log|V|),但因爲它是合理的假設|E| > |V|,你有O(|E|log|V|)

  • 隨後趕來的斐波那契堆,那就是你有O(1)的插入/更新分期時間/搜索最小值,但O(log|V|)分期時間進行刪除最少,爲您提供Dijkstra算法結合的O(|E| + |V|log|V|)目前最知名的時間。

最後,對於解決O(|V|log|V|)最壞情況下的時間複雜度的單源最短路徑問題的算法是不可能的,如果(|V|log|V| < |E|),因爲這個問題有一個結合的O(|E| + |V|)瑣碎較低的時間即你需要檢查每個頂點至少一次解決問題。

1

通過使用BST或堆提高Dijkstra將導致時間複雜度,如O(|E|log|V|)O(|E|+|V|log|V|),請參閱Dijkstra's running time。每個邊緣都必須在某個時候檢查。