Dijkstra std :: priority_queue的最短路径算法性能Vs std :: set

时间:2017-04-16 06:35:36

标签: c++ algorithm performance graph stl

我想了解这些容器在时间复杂性方面的主要区别。 我已经尝试了3种Dijkstra算法的实现,如下所述:

1-使用简单数组作为队列

2-使用STL priority_queue

3-设置STL

我测试的图表相当大,它包含超过150000个顶点,定向并且所有边缘的权重都是正的。

我得到的结果如下:

1 - 使用数组算法非常慢 - >这是预期的

2 - 使用STL priority_queue算法运行速度比数组快得多 - >这也是预期的

3 - 使用STL设置算法以非常快的速度运行,我说的话比priority_queue快100倍 - >我没想到会看到这种巨大的表现......

知道std :: priority_queue和std :: set是存储元素的数据容器,并且两者的插入复杂度基本相同O(log n),我不明白它们之间的这种巨大的性能差异。你对此有任何解释吗?

感谢您的帮助,

编辑: 这是我实现的摘要:

使用std :: set:

unsigned int Graphe::dijkstra(size_t p_source, size_t p_destination) const {

....


set<pair<int, size_t>> set_vertices;


vector<unsigned int> distance(listAdj.size(),
        numeric_limits<unsigned int>::max());


vector < size_t
        > predecessor(listAdj.size(),
                numeric_limits < size_t > ::max());


distance[p_source] = 0;
set_vertices.insert( { 0, p_source });


while (!set_vertices.empty()) {

    unsigned int u = set_vertices.begin()->second;


    if (u == p_destination) {
        break;
    }

    set_vertices.erase( { distance[u],
            u });


    for (auto itr = listAdj[u].begin();
            itr != listAdj[u].end(); ++itr) {


        int v = itr->destination;
        int weigth = itr->weigth;


        if (distance[v]
                > distance[u] + weigth) {


            if (distance[v]
                    != numeric_limits<unsigned int>::max()) {
                set_vertices.erase(
                        set_vertices.find(
                                make_pair(distance[v],
                                        v)));
            }

            distance[v] = distance[u] + weigth;

            set_vertices.insert( { distance[v],
                    v });

            predecessor[v] = u;
        }
    }
}

....

return distance[p_destination];}

并使用priority_queue:

unsigned int Graphe::dijkstra(size_t p_source, size_t p_destination) const {

...

typedef pair<size_t, int> newpair;

priority_queue<newpair, vector<newpair>, greater<newpair> > PQ;

vector<unsigned int> distance(listAdj.size(),
        numeric_limits<unsigned int>::max());


vector < size_t
        > predecessor(listAdj.size(),
                numeric_limits < size_t > ::max());


distance[p_source] = 0;
PQ.push(make_pair(p_source, 0));

while (!PQ.empty()) {


    unsigned int u = PQ.top().first;


    if (u == p_destination) {
        break;
    }

    PQ.pop();


    for (auto itr = listAdj[u].begin();
            itr != listAdj[u].end(); ++itr) {


        int v = itr->destination;
        int weigth = itr->weigth;


        if (distance[v]
                > distance[u] + weigth) {

            distance[v] = distance[u] + weigth;

            PQ.push(
                    make_pair(v, distance[v]));

            predecessor[v] = u;
        }
    }
}
...

return distance[p_destination];}

2 个答案:

答案 0 :(得分:1)

跳过

您使用优先级队列确实使工作加倍了。

您正在两次插入队列,因为您无法修改或删除。那是正常且必要的,因为你做不到。

但是当那些旧值从队列中出来时,您需要“跳过while循环的迭代”。

类似的东西:

if (PQ.top().second != distance[PQ.top().first]) continue;  // It's stale! SKIP!!

答案 1 :(得分:0)

std::priority_queue的基础数据结构是最大堆,std::set是自平衡二进制搜索 - 基本上是C ++的红黑树。因此,它们都可以确保插入,删除和更新操作的O(logn)时间复杂度。

但是,正如我所提到的,std::set的平衡二叉搜索树正在自动平衡,以保持其高度对数的节点数,从而确保对数查询的复杂性,无论插入顺序如何或在任何操作之后。 std::priority_queue不是自我平衡的,根据插入顺序可能非常平坦。虽然自我平衡有它自己的成本,所以在删除顶部之后堆积,我认为这是性能提升的原因。

希望它有所帮助!