如何从std::map<int, float>
创建vector<float>
,以便地图包含来自向量的k个最高值,其中键是向量中的值的索引。
一种天真的方法是遍历向量(O(n)),提取和擦除(O(n))最高元素k次(O(k)),导致O(k * n)的复杂性^ 2),我认为这是次优的。
更好的是复制(O(n))并删除最小值,直到大小为k。这将导致O(n ^ 2)。还是多项式......
有什么想法吗?
答案 0 :(得分:2)
以下应该做的工作:
#include <cstdint>
#include <algorithm>
#include <iostream>
#include <map>
#include <tuple>
#include <vector>
// Compare: greater T2 first.
struct greater_by_second
{
template <typename T1, typename T2>
bool operator () (const std::pair<T1, T2>& lhs, const std::pair<T1, T2>& rhs)
{
return std::tie(lhs.second, lhs.first) > std::tie(rhs.second, rhs.first);
}
};
std::map<std::size_t, float> get_index_pairs(const std::vector<float>& v, int k)
{
std::vector<std::pair<std::size_t, float>> indexed_floats;
indexed_floats.reserve(v.size());
for (std::size_t i = 0, size = v.size(); i != size; ++i) {
indexed_floats.emplace_back(i, v[i]);
}
std::nth_element(indexed_floats.begin(),
indexed_floats.begin() + k,
indexed_floats.end(), greater_by_second());
return std::map<std::size_t, float>(indexed_floats.begin(), indexed_floats.begin() + k);
}
让我们测试一下:
int main(int argc, char *argv[])
{
const std::vector<float> fs {45.67f, 12.34f, 67.8f, 4.2f, 123.4f};
for (const auto& elem : get_index_pairs(fs, 2)) {
std::cout << elem.first << " " << elem.second << std::endl;
}
return 0;
}
输出:
2 67.8
4 123.4
答案 1 :(得分:1)
您可以保留到目前为止k个最高值的列表,并为向量中的每个值更新它,这会将您降低到O(n * log k)(假设每次更新的日志k为最高值列表)或者,对于天真的列表,O(kn)。
你可能接近O(n),但假设k可能很小,可能不值得努力。
答案 2 :(得分:0)
也许我没有得到它,但如果增量方法不是一个选项,为什么不使用 std::sort
std::partial_sort
?
那应该是o(n log k),并且因为k很可能很小,所以实际上是o(n)。
编辑:感谢Mike Seymour的更新。 编辑(之二):
这个想法是使用中间向量进行排序,然后将其放入地图中。试图减少计算的顺序只能用于大量数据,所以我猜复制时间(在o(n)中)可能会在背景噪声中丢失。
编辑(之二):
这实际上是所选答案所做的,没有理论解释:)。
答案 3 :(得分:0)
您的最佳解决方案将具有 O(n + k * log(k))的复杂度,因为对k元素进行排序可以减少到此,并且您将不得不查看每个元素至少一次。
我想到了两种可能的解决方案:
迭代向量,同时将所有元素添加到有界(大小k)优先级队列/堆中,同时保留其索引。
使用包含原始索引(即std::vector<std::pair<float, std::size_t>>
)创建矢量副本,并使用std::nth_element
使用仅比较第一个元素的比较器将k个最高值移动到前面。然后将这些元素插入到目标地图中。具有讽刺意味的是,最后一步在总体复杂性中增加了k * log(k),而nth_element是线性的(但会置换你的指数)。