我希望了解这些容器在时间复杂度方面的主要区别。我尝试了以下三种Dijkstra算法的实现:
1- 使用简单数组作为队列
2- 使用STL优先队列
3- 使用STL集合
我测试的图形相当大,包含超过150000个顶点,都是有向的,边的权重都是正数。
我得到的结果如下:
1- 用数组算法非常慢——这是预期的;
2- 使用STL优先队列比数组运行快得多——这也是预期的;
3- 使用STL集合算法运行速度非常快,我说的是比优先队列快上几百倍——我没有预料到会有这么大的性能差异……
知道std::priority_queue和std::set都是存储元素的数据容器,它们的插入复杂度基本相同为O(log n),我不理解它们之间的这种巨大性能差异。你有关于这个的任何解释吗?
感谢您的帮助,
编辑:
这是我的实现摘要:
使用std::set:
1- 使用简单数组作为队列
2- 使用STL优先队列
3- 使用STL集合
我测试的图形相当大,包含超过150000个顶点,都是有向的,边的权重都是正数。
我得到的结果如下:
1- 用数组算法非常慢——这是预期的;
2- 使用STL优先队列比数组运行快得多——这也是预期的;
3- 使用STL集合算法运行速度非常快,我说的是比优先队列快上几百倍——我没有预料到会有这么大的性能差异……
知道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];}
priority_queue
和set
时,更新顶点距离步骤可能会导致巨大的差异。您可以使用set
轻松更新距离,但是如果不添加相同的顶点多次并具有不同的距离,则无法使用priority_queue
进行更新。 - DAle