Dijkstra 最短路径算法性能 std::p riority_queue VS std::set.
Dijkstra shortest path algorith performance of std::priority_queue Vs std::set
我想了解这些容器在时间复杂度方面的主要区别。 我已经尝试了 3 种 Dijkstra 算法的实现,如下所述:
1-使用简单数组作为队列
2- 带 STL priority_queue
3- 带 STL 设置
我测试过的图非常大,它包含超过 150000 个顶点,定向且边缘的所有权重都是正数。
我得到的结果如下:
1 - 使用数组,算法非常慢 ->这是预期的
2 - 使用 STL priority_queue算法的运行速度比数组快得多 - 这也是预期的>
3 - 使用 STL 设置算法运行速度非常快,我说的是比priority_queue快 100 倍的夫妇 ->我没想到会看到如此巨大的性能......
知道 std::p riority_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];}
跳过
您在优先级队列上加倍工作非常糟糕。
您正在重复插入队列,因为您无法修改或删除。这是正常和必要的,因为你不能。
但是,当这些旧值从队列中出来时,您需要"跳过while循环的迭代"。
像这样:
if (PQ.top().second != distance[PQ.top().first]) continue; // It's stale! SKIP!!
std::priority_queue
的底层数据结构是一个最大堆,std::set
它的自平衡二叉搜索 - 基本上是一棵红黑树C++。因此,它们都确保了插入,删除和更新操作O(logn)
时间复杂性。
但是,正如我所提到的,std::set
的平衡二叉搜索树会自动平衡以保持其节点数的高度对数,从而确保对数查询的复杂性,无论插入顺序或任何操作后。std::priority_queue
不是自平衡的,根据广告顺序的不同,可能非常平坦。虽然自平衡有自己的成本,所以在删除顶部后堆积,但我认为这就是性能提升的原因。
希望对您有所帮助!
- 为什么这个 std::queue/指向结构的指针列表直到 List.Size() == 0 才释放内存?
- 将参数打包的参数传递到 std::queue 中,以便稍后使用不同的函数调用
- 我可以擦除 std::queue 中间的节点吗?
- 获取大小时是否必须锁定 std::queue?
- 销毁 std::queue 会导致内存错误
- 如何将一个 std::queue 的内容附加到另一个
- std :: queue :: pop()在其std :: unique_ptr数据上操作
- 通过 std::queue 中的元素的值获取元素的索引
- 像std::queue这样的c++标准库容器是否保证是可重入的
- 将元素推入 std::queue 时避免复制
- std::queue 不保留数据
- 生产者-消费者队列 - std::queue 或用户编写的链接列表
- std::queue::empty() not working?
- 在 Unix 中使用 std::queue() 时出现问题
- 为什么std::queue不是线程安全的
- std::queue::size() 可以在 size() == 0 的 pop() 之后返回一个巨大的数字
- 我看不到从 std::queue 中获取元素的方法
- std::stack和std::queue缓存友好
- malloc创建std::queue时的默认大小
- c++ std::queue pop()和析构函数