【发布时间】:2021-09-09 04:11:56
【问题描述】:
我注意到std::priority_queue 以排序方式存储元素。显然,以排序方式存储元素将是一个糟糕的设计选择,因为 push 和 pop 的时间复杂度会飙升至 O(n)。但事实证明std::priority_queue 在线性时间内神奇地对元素进行排序。
这是我用于测试的代码。
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>
#include <chrono>
#include <random>
#include <climits>
#include <fstream>
#include <ios>
int main() {
int size = 10'000'000;
std::random_device rd;
std::mt19937 mt{rd()};
std::uniform_int_distribution<int> uid{1, INT32_MAX};
std::vector<int> vs;
for (int i = 0; i < size; ++i) {
vs.push_back(uid(mt));
}
// Measures time taken by make_heap
std::vector<int> vs1{vs};
auto start = std::chrono::system_clock::now();
std::make_heap(vs1.begin(), vs1.end());
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time taken by make_heap: " << diff.count() << std::endl;
// Measures time taken by priority_queue
std::vector<int> vs2{vs};
start = std::chrono::system_clock::now();
std::priority_queue<int, std::vector<int>, std::greater<int>> qs{vs2.begin(), vs2.end()};
end = std::chrono::system_clock::now();
diff = end - start;
std::cout << "Time taken by priority_queue: " << diff.count() << std::endl;
// Measures time taken by sort
std::vector<int> vs3{vs};
start = std::chrono::system_clock::now();
std::sort(vs3.begin(), vs3.end());
end = std::chrono::system_clock::now();
diff = end - start;
std::cout << "Time taken by sort: " << diff.count() << std::endl;
std::ofstream ofile;
ofile.open("priority_queue_op.txt", std::ios::out);
for (int i = 0; i < size; ++i) {
ofile << qs.top() << std::endl;
qs.pop();
}
ofile.close();
ofile.open("sort_op.txt", std::ios::out);
for (auto& v : vs3)
ofile << v << std::endl;
ofile.close();
// run `diff priority_queue_op.txt sort_op.txt`
return 0;
}
$ g++ -O3 test.cpp -o test
$ ./test
Time taken by make_heap: 0.133292
Time taken by priority_queue: 0.151002
Time taken by sort: 0.910701
$ diff priority_queue_op.txt sort_op.txt
$
从上面的输出来看,std::priority_queue 似乎是在线性时间对元素进行排序。
This site 建议std::priority_queue 使用标准库中的堆函数在内部管理堆。甚至源代码也证实了这一点。
template<typename _InputIterator>
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x = _Compare(),
_Sequence&& __s = _Sequence())
: c(std::move(__s)), comp(__x)
{
__glibcxx_requires_valid_range(__first, __last);
c.insert(c.end(), __first, __last);
std::make_heap(c.begin(), c.end(), comp);
}
插入过程用于插入元素,后跟std::make_heap 以构建堆。那么元素是如何神奇地排序的呢?即使有什么事情是如何在线性时间内发生的?
【问题讨论】:
-
为什么你认为它是排序的?每当您调用
pop时,容器都会调用std::pop_heap以使下一个最多的元素进入“顶部”位置 -
构建堆可以在线性时间内完成,尽管重复删除以按排序顺序查看它们不能。请参阅stackoverflow.com/questions/9755721/…,了解
std::make_heap如何在线性时间内执行。 -
无关:使用
system_clock进行基准测试可能会因系统时钟的更改而混乱。steady_clock通常是更好的选择。 -
@NathanOliver 刚刚意识到它并没有以排序方式存储元素,而只是输出使它看起来好像元素已排序。这是因为我每次都打印顶部元素导致排序数组
标签: c++ sorting c++11 priority-queue