![](/img/trans.png)
[英]What is the most efficient way to project structured data to a std::vector?
[英]What is the most efficient way of copying elements that occur only once in a std vector?
我有一个std向量与这样的元素:
[0 , 1 , 2 , 0 , 2 , 1 , 0 , 0 , 188 , 220 , 0 , 1 , 2 ]
找到并复制在此向量中仅出现一次的元素的最有效方法是什么,不包括强力O(n ^ 2)算法? 在这种情况下,新列表应包含[188, 220]
unordered_map<DataType, Count> count;
count[value]++;
排序count[value]++;
count
映射复制键,其值为1。 这是O(n)
。 你有哈希所以对于小数据集法线贴图可能更有效,但从技术上来说它将是O(n log n)
。
这是离散数据集的好方法。
代码示例:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v{1,1,2,3,3,4};
unordered_map<int,int> count;
for (const auto& e : v) count[e]++;
vector<int> once;
for (const auto& e : count) if(e.second == 1) once.push_back(e.first);
for (const auto& e : once) cout << e << '\n';
return 0;
}
我尝试了一些想法。 但我没有看到map
的方法。 unordered_multiset
几乎是一个很好的方式...除了它不允许你迭代键。 它有一种检查密钥计数的方法,但是你需要另一套只用于探测密钥的方法。 我不认为这是一种更简单的方式。 在现代的c ++中, auto
计数很容易。 我也查看过algorithm
库,但是我没有找到任何可以有条件地转换元素的transfrom
, copy_if
, generate
等等(如果count为1,则为map entry - > value)。
普遍优化的算法非常少。 哪种算法效果最好通常取决于正在处理的数据的属性。 删除重复项就是一个这样的例子。
是v
小,大多具有唯一值填?
auto lo = v.begin(), hi = v.end();
std::sort(lo, hi);
while (lo != v.end()) {
hi = std::mismatch(lo + 1, v.end(), lo).first;
lo = (std::distance(lo, hi) == 1) ? hi : v.erase(lo, hi);
}
是v
小,并重复大多是填补?
auto lo = v.begin(), hi = v.end();
std::sort(lo, hi);
while (lo != v.end()) {
hi = std::upper_bound(lo + 1, v.end(), *lo);
lo = (std::distance(lo, hi) == 1) ? hi : v.erase(lo, hi);
}
是v
巨大 ?
std::unordered_map<int, bool> keyUniqueness{};
keyUniqueness.reserve(v.size());
for (int key : v) {
bool wasMissing = keyUniqueness.find(key) == keyUniqueness.end();
keyUniqueness[key] = wasMissing;
}
v.clear();
for (const auto& element : keyUniqueness) {
if (element.second) { v.push_back(element.first); }
}
等等。
@ luk32的答案绝对是解决这个问题最有效的方法。 但是,如果您的内存不足并且无法承受unordered_map
,则还有其他方法可以执行此操作。
您可以使用std::sort()
来首先对向量进行排序。 然后可以在一次迭代中找到非重复项。 整体复杂性为O(nlogn)
。
如果问题略有不同,并且您知道只有一个非重复元素,则可以使用此代码 (Java中的代码)。 这里的困惑是O(n)
。
由于你使用std::vector
,我认为你想要最大化它的所有好处,包括引用局部性 。 为了做到这一点,我们需要在这里输入一些内容。 我对以下代码进行了基准测试......
我这里有一个线性O(n)
算法(实际上是O(nlog(n))
),它有点像brian的答案,但我使用的是OutputIterators而不是就地执行它。 前提条件是它已经排序。
template<typename InputIterator, typename OutputIterator>
OutputIterator single_unique_copy(InputIterator first, InputIterator last, OutputIterator result){
auto previous = first;
if(previous == last || ++first == last) return result;
while(true){
if(*first == *previous)
while((++first != last) && (*first == *previous));
else
*(result++) = *previous;
if(first == last) break;
previous = first;
++first;
}
return ++result;
}
以下是一个示例用法:
int main(){
std::vector<int> vm = {0, 1, 2, 0, 2, 1, 0, 0, 1, 88, 220, 0, 1, 2, 227, -8};
std::vector<int> kk;
std::sort(vm.begin(), vm.end());
single_unique_copy(vm.begin(), vm.end(), std::back_inserter(kk));
for(auto x : kk) std::cout << x << ' ';
return 0;
}
正如预期的那样,输出是:
-8, 88, 220, 227
您的使用案例可能与我的不同,因此,首先介绍...... :-)
编辑:
i % 5
重复。 9.34
秒和我的: 7.80
秒 2.71
秒并且开采0.52
秒 对于较小的数字,差异仍然存在,直到它成为非关键代码
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.