消除 std::vector 中的重复项

eliminating duplicates in std::vector

本文关键字:std vector 消除      更新时间:2023-10-16

我有一个非常大的std::vector of std::vectors,其中包含固定数量的无符号整数。

uint 的所有向量都按升序排序。

我目前消除重复向量的方法是

unsigned int i = 0;
while ( i < new_combs.size() )
{
  unsigned int j = i + 1;
  while ( j < new_combs.size() )
  {
     unsigned int k = 0;
     while ( k < new_combs.at(i).size() && new_combs.at(i).at(k) == new_combs.at(j).at(k) )
        ++k;
     if ( k == new_combs.at(j).size() )
        new_combs.erase(new_combs.begin() + j);
     else
        ++j;
  }
  ++i;
}

在这里,new_combs是包含上述向量的向量。

如果向量的向量未排序,是否有更有效的方法来消除重复项?

一个较短的方法是使用 <algorithm>

std::sort(new_combs.begin(), new_combs.end());
new_combs.erase(std::unique(new_combs.begin(), new_combs.end()), new_combs.end());

除非你特别需要std::vector,否则你可以使用std::set来避免重复。

你有没有考虑过使用 std::set?它是有序的,不允许一开始重复。

如果向量未排序,则无能为力。但是,如果它被排序,您可以使用算法中定义的唯一方法:

new_combs.erase(unique(new_combs.begin(), new_combs.end()), new_combs.end());

渐近地,您的算法看起来像通常的 O(n) 实现,因此是最佳的。(即使我不明白你对ij的对角化策略,以及为什么你只擦除,但从不移动元素。您的代码非常不清楚。但是,您正在复制 STL,唯一循环的较短版本为:

struct unique {
    template <class C>
    void operator()( C& c ) {
         c.erase( std::unique( c.begin(), c.end() ), c.end() );
    }
};
std::for_each( new_combs.begin(), new_combs.end(), unique() );

我同意 Luchian Grigorie 的回答,但您也可以考虑将整个外部vector转换为 unordered_set ,这是一个 O(n) 操作,前提是子向量的哈希不会太不平衡(而不是用于排序的平均 O(n*log(n))。您甚至可以在unordered_set中使用指向子向量的指针,以避免不必要的复制。对于大量数据,这可能是一个重要的性能差异。

这个例子说明了使用你自己的哈希函数和指针的基本思想(它处理stringvector并使用unordered_map,而不是unordered_set,但你应该能够相当容易地修改它以满足你的需要)。

您的代码中有几个元素敲响了我对性能的警钟。

首先,您使用的是向量。 从矢量中擦除元素总是很慢。您可以考虑使用不同的容器 (std::list) 或调整您的代码,以便您有一个特殊的值(例如零或 -1)。

其次,您可以使用 std::set 或 std::unordered_set 来保留您已经遇到的值。这样,您只需遍历一次向量。

编辑:忘记这个答案。 我误读了这个问题,认为必须删除重复值(而不是重复向量)。

尽管如此,对所给出的评论的一些反应:

  • @Jerry:我同意向量在大多数情况下比列表快,但前提是向量的大小有限。 如果向量包含 100 万个元素,您需要删除第 3 个,然后是第 5 个,然后是第 10 个,...你最终会移动很多元素。 在这种情况下,列表可能会更快。
  • @James:在原始问题中,元素不是从向量的末尾删除的,而是从中间删除的。 如果向量非常大(假设有 100 万个元素),那么删除元素仍然可能成为瓶颈。 但是,我同意比使用排序,然后使用唯一排序可能更快。