复制在std向量中只出现一次的元素的最有效方法是什么?

时间:2016-02-05 03:24:50

标签: c++ c++11 stdvector

我有一个带有这样元素的标准向量:

[0 , 1 , 2 , 0 , 2 , 1 , 0 , 0 , 188 , 220 , 0 , 1 , 2 ]

查找和复制在此向量中仅出现一次的元素的最有效方法是什么,不包括强力O(n ^ 2)算法?在这种情况下,新列表应包含[188, 220]

4 个答案:

答案 0 :(得分:9)

  1. 制作unordered_map<DataType, Count> count;
  2. 迭代输入向量,增加每个值的计数。排序count[value]++;
  3. 迭代值为1的count地图复制键。
  4. 它是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库,但我还没找到任何可以有条件地转换元素的transfromcopy_ifgenerate等。 (地图条目 - &gt;值,如果count为1)。

答案 1 :(得分:8)

极少有普遍优化的算法。哪种算法效果最好通常取决于正在处理的数据的属性。删除重复项就是一个这样的例子。

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 gigantic

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); }
}

等等。

答案 2 :(得分:1)

@ luk32的回答绝对是解决这个问题最有效的方法。但是,如果你的记忆力不足并且无法承担unordered_map,那么还有其他方法可以做到。{/ p>

您可以先使用std::sort()对矢量进行排序。然后可以在一次迭代中找到非重复项。整体复杂度为O(nlogn)

如果问题略有不同,并且您知道只有一个非重复元素,则可以使用this code(Java中的代码)。这里的困惑是O(n)

答案 3 :(得分:1)

由于您使用std::vector,我认为您希望最大限度地发挥其所有优势,包括reference locality。为了做到这一点,我们需要在这里输入一些内容。我对下面的代码进行了基准测试......

我这里有一个线性O(n)算法(有效O(nlog(n))),它有点像布莱恩的回答,但我使用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

您的使用案例可能与我的不同,因此,首先介绍......: - )

编辑:

  • 使用luk32的算法和我的...使用1300万个元素...... 按降序创建,每i % 5重复一次。
  • 在调试版本中,luk32:9.34秒和我的:7.80
  • 在-O3下,luk32:2.71秒,我的0.52
  • Mingw5.1 64位,Windows10,1.73Ghz Core i5 4210U,6GB DDR3 1600Mhz RAM
  • 此处为基准,http://coliru.stacked-crooked.com/a/187e5e3841439742

对于较小的数字,差异仍然存在,直到它成为非关键代码