从载体中删除重复项的最快方法<>

Fastest way to remove duplicates from a vector<>

本文关键字:方法 lt gt 删除      更新时间:2023-10-16

正如标题所说,我脑海中有一些方法可以做到这一点,但我不知道哪种方法最快。

假设我们有一个值为的:vector<int> vals

1

添加我的vals

sort(vals.begin(), vals.end());
auto last = unique(vals.begin(), vals.end());
vals.erase(last, vals.end());

2

添加vals后转换为集合:

set<int> s( vals.begin(), vals.end() );
vals.assign( s.begin(), s.end() );

3

当我添加vals时,我会检查它是否已经在我的向量中:

if( find(vals.begin(), vals.end(), myVal)!=vals.end() )
    // add my val

4

从一开始就使用一套

好的,我有这4种方法,我的问题是:

1从1、23哪个最快
24比前3快吗
3在2将向量转换为集合后,使用集合做我需要做的事情更方便,还是应该做vals.assign( .. )并继续使用我的向量?

问题1:1和2都是O(n log n),3是O(n^2)。在1和2之间,这取决于数据。

问题2:4也是O(n log n),如果有很多重复项,它可能比1和2更好,因为它只存储每个的一个副本。想象一下,一百万个值都是相等的。

问题3:好吧,这真的取决于你需要做什么。

在不知道更多的情况下,唯一可以说的是,你的备选数字3比其他数字更差。

如果您使用的是C++11并且不需要排序,那么您可以使用std::unordered_set,它是一个哈希表,并且可能比std::set快得多。

选项1将击败所有其他选项。复杂度仅为O(N log N),向量的连续内存使常数因子保持在较低水平。

std::set通常会受到非连续分配的影响。访问它们不仅速度慢,而且创建它们也需要大量时间。

尽管(1)值得一看,但这些方法都有缺点

但是,看看第五个选项:请记住,您可以使用data()函数访问矢量的数据缓冲区。然后,记住不会发生重新分配,因为矢量只会越来越小,应用你在学校学到的算法:

unduplicate(vals.data(), vals.size());
void unduplicate(int* arr, std::size_t length) /*Reference: Gang of Four, I think*/
{
    int *it, *end = arr + length - 1;
    for (it = arr + 1; arr < end; arr++, it = arr + 1){
        while (it <= end){
            if (*it == *arr){
                *it = *end--;
            } else {
                ++it;
            }
        }
    }
}

如果需要的话,在最后调整向量的大小。这永远不会比O(N^2)更糟,因此优于插入排序或先排序后删除的方法。

如果你能接受的话,你的第四个选择可能是个主意。否则就用我20世纪60年代的算法。

我最近遇到了类似的问题,并对124以及4unordered_set版本进行了实验。结果表明,性能最好的是后者,4unordered_set代替set

顺便说一句,如果考虑到setsort都有点过头了,这一经验发现并不太令人惊讶:它们保证了不相等元素的相对顺序。例如,输入4,3,5,2,4,3将导致排序输出唯一值2,3,4,5。如果您可以使用任意顺序的唯一值(即3,4,2,5),则这是不必要的。当您使用unordered_set时,它不保证顺序,只保证唯一性,因此它不必执行确保不同元素顺序的额外工作。