【问题标题】:Complexity Of Dijkstra's algorithmDijkstra 算法的复杂性
【发布时间】:2016-08-04 16:28:34
【问题描述】:

我从许多资料中了解到,如果使用一种简单的方法来获取最小元素(线性搜索),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) 中运行。

【问题讨论】:

    标签: c++ algorithm time-complexity dijkstra shortest-path


    【解决方案1】:

    为了了解 Dijkstra 算法的时间复杂度,我们需要研究对用于实现 Frontier 集的数据结构(即您的算法中用于 minv 的数据结构)执行的操作:

    • 插入
    • 更新
    • 查找/删除最小值

    在整个算法期间,数据结构上总共发生了O(|V|) 插入、O(|E|) 更新、O(|V|) 查找/删除最小值。

    • 最初 Dijkstra 使用未排序的数组实现了 Frontier 集。因此,插入和更新是O(1),但查找/删除最小值是O(|V|),导致O(|E| + |V|^2),但由于|E| &lt; |V|^2,你有O(|V|^2)

    • 如果使用二进制最小堆来实现边界集,则所有操作都有log(|v|),导致O(|E|log|V| + |V|log|V|),但由于假设|E| &gt; |V| 是合理的,所以你有@987654334 @。

    • 然后是斐波那契堆,其中您有 O(1) 用于插入/更新/查找最小值的摊销时间,但 O(log|V|) 用于删除最小值的摊销时间,为您提供当前最知名的时间界限 O(|E| + |V|log|V|)对于 Dijkstra 算法。

    最后,如果(|V|log|V| &lt; |E|),解决O(|V|log|V|) 最坏情况时间复杂度中的单源最短路径问题的算法是不可能的,因为问题具有O(|E| + |V|) 的微不足道的下限时间,即您需要检查每个顶点和边至少一次来解决问题。

    【讨论】:

    • +1。很好的答案。一个问题。在每一步我们都有 O|E|在最坏的情况下更新?为什么?我的意思是,如果我们要说更新的摊销成本,我们可以说什么? O(|E| / |N| )?
    • @MioUnio O(|E|) 更新整个算法,而不是每一步。
    • 那么每个顶点更新的摊销成本是多少?
    • 摊销成本仅与斐波那契堆相关。对于未排序的数组,每次更新都是O(1),因此所有更新都是O(|E|)。对于二进制最小堆,每个更新都是O(log|V|),因此所有更新都是O(|E|log|V|)。对于斐波那契堆,我相信单个更新(减少键)的最坏情况可能高达O(|V|),但可以证明“平均”在算法期间需要O(1),因此摊销O(1) 每次更新的时间,或O(|E|) 的所有更新时间。
    • 请参阅本讲座的第 23 页courses.cs.washington.edu/courses/cse373/18wi/files/slides/… 对于未排序的数组,我们可以说 O(E/V) 其中 V 是顶点数是摊销分析?
    【解决方案2】:

    使用 BST 或堆改进 Dijkstra 将导致时间复杂性,例如 O(|E|log|V|)O(|E|+|V|log|V|),请参阅 Dijkstra's running time。每条边都必须在某个时间点进行检查。

    【讨论】:

      猜你喜欢
      • 2016-07-11
      • 2019-07-31
      • 2021-04-22
      • 1970-01-01
      • 2020-09-22
      • 2020-04-15
      • 2014-12-20
      • 2019-05-14
      相关资源
      最近更新 更多