【问题标题】:eliminating duplicates in std::vector消除 std::vector 中的重复项
【发布时间】:2012-04-22 17:36:21
【问题描述】:

我有一个非常大的 std::vector 的 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 是一个包含上述向量的向量。

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

【问题讨论】:

标签: c++ algorithm stdvector


【解决方案1】:

更短的方法是使用&lt;algorithm&gt;

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 来避免重复。

【讨论】:

  • 不是排序也至少 O(n²) 吗?
  • @stefan 您的解决方案是 O(n^2)。而且 std::sort 的速度出奇的快。
  • 我知道我的算法是 O(n²),但这不是 std::sort 比 O(n²) 快的论据;-)
  • @stefan std::sort 在最坏的情况下是 O(n^2)。这是一个快速排序,通常是 O(n*log(n))。
  • @stefan 排序平均为 n*log(n)。
【解决方案2】:

你考虑过使用 std::set 吗?它是有序的,不允许以重复开头。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

      尽管如此,对给出的 cmets 的一些反应:

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

      【讨论】:

      • 我不明白你所说的“没有任何特殊价值”是什么意思,我的向量都是相同大小的,并且大小总是大于 0
      • 您是否曾经将向量与列表进行过比较?您可能想阅读previous question,包括答案和链接。底线:即使理论上应该更优越,list 通常也比 vector 慢。
      • 从矢量末尾擦除元素很慢。使用带有排序向量和std::unique 的解决方案可能比使用任何形式的地图(它们是基于节点的容器,而且速度非常慢)要快得多。
      【解决方案5】:

      渐近地,您的算法看起来像通常的 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() );
      

      【讨论】:

      • 我为什么要搬家?我不在乎对向量进行排序
      • @stefan:您想删除重复项,并且向量是连续的,因此您必须在某些时候向上移动一些元素。
      【解决方案6】:

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

      This example 说明了使用您自己的哈希函数和指针的基本思想(它处理strings 的vector 并使用unordered_map,而不是unordered_set,但您应该能够公平地修改它轻松满足您的需求)。

      【讨论】:

        猜你喜欢
        • 2011-07-20
        • 2019-07-11
        • 2011-05-25
        • 2022-10-24
        • 1970-01-01
        • 2018-02-25
        • 2023-03-03
        • 2014-05-24
        • 2020-12-26
        相关资源
        最近更新 更多