【问题标题】:Removing duplicates in an array while preserving the order in C++ [duplicate]删除数组中的重复项,同时保留 C++ 中的顺序 [重复]
【发布时间】:2010-08-30 08:08:42
【问题描述】:

可能重复:
How to make elements of vector unique? (remove non adjacent duplicates)

是否有任何标准算法作为 STL 算法的一部分提供,可以在保留顺序的同时从数组中删除重复项。例如,如果我在删除重复项后有一个类似int a[] = {2,1,3,1,4,2}; 的数组,它应该是a[] = {2,1,3,4};。我不能使用std::unique,因为数组没有排序。其他解决方案,例如将其插入 std::set 我失去了顺序,因为元素将被排序。是否有任何其他算法组合我可以使用或者我必须自己编写代码?

【问题讨论】:

  • 这在算法上效率很低,你知道的。你可以在 O(N^2) 中使用 O(1) 内存,或者我认为在 O(N * log N) 中使用 O(N) 内存。
  • @GMan:如果你同时进行桶排序 O(2^32) 个桶,你可以在 O(n) 内完成。

标签: c++ algorithm stl


【解决方案1】:

对此没有标准算法,但实现起来相当容易。原则是保留您目前看到的项目的std::set,并在复制到新向量或数组时跳过重复项。这在 O(n lg n) 时间和 O(n) 内存中运行。如果您使用的是 C++0x,则可以通过使用 std::unordered_set 来将其缩短到 O(n) 时间。这使用哈希表而不是二叉树,应该更快。

【讨论】:

  • 哈希从未有过O(1) 的性能。而且永远不会。有一个例外:hash 有O(1) 查找/插入时间,只有在被迷惑的研发人员坐在厚墙后面的梦想中,可以很好地避免客户反馈。
  • 我认为您对O(1) 的含义有些误​​解。这并不意味着快,它意味着无论我们谈论的变量(这里是容器的大小)都是恒定的。请注意,如果您有大量项目,您可能更喜欢布隆过滤器,而不是使用哈希表。
  • 布隆过滤器有可能出现误报。只有当 a) 阳性很少见并且 b) 查找真正的后备存储的成本很高(或误报是可以接受的)时,它才真正有帮助。如果真正的支持集保存在内存中,那就不是胜利。
【解决方案2】:

由于问题相对“复杂”,我不会尝试仅使用标准算法来强制解决(因为没有特殊算法可以解决您的问题。您可能会使用 remove_if、find 和 bind2nd 或某物)。 对于自己实现算法,您基本上有两种选择,通常是内存与速度的权衡。 第一个解决方案是迭代向量并搜索和删除当前项目的重复项。这是 CPU 密集型方法。 也许更快的方法是创建第二个向量(与第一个向量大小相同以最小化内存重新分配)并将找到的项目存储在其中。然后,对于原始向量的每次迭代,只需要搜索较短的第二个向量,以确定是否应该删除当前项。 第一种方法适用于每个迭代器,而第二种方法仅限于随机访问迭代器。 以下是实现:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

template<typename T>
void remove_duplicates_ordered_mem_intensive(T &container)
{
   std::vector<typename T::value_type> items;
   items.reserve (container.size());

   typename T::iterator i = container.begin();
   while (i != container.end())
   {
      if (find (items.begin(), items.end(), *i) != items.end())
         i = container.erase(i);
      else
      {
         items.push_back(*i);
         ++i;
      }
   }
} 

template<typename T>
void remove_duplicates_ordered_slow(T &container)
{
   typename T::iterator i = container.begin();
   while (i != container.end())
   {
      typename T::iterator f = i;
      ++f;
      while (f != container.end())
      {
         if (*f == *i)
            f = container.erase(f);
         else
            ++f;
      }
      ++i;
   }
} 

int main ()
{
   vector<int> v;
   v.push_back (2);
   v.push_back (1);
   v.push_back (3);
   v.push_back (1);
   v.push_back (4);
   v.push_back (2); 

   cout << "Old:\n";
   for (vector<int>::const_iterator i = v.begin(); i != v.end(); ++i)
      cout << *i << endl;


   vector<int> a (v), b (v);
   remove_duplicates_ordered_mem_intensive (a);
   remove_duplicates_ordered_slow (b); 

   cout << "\nRemoved duplicates with intensive memory usage:\n";
   for (vector<int>::const_iterator i = a.begin(); i != a.end(); ++i)
      cout << *i << endl; 

   cout << "\nRemoved duplicates somewhat slower, without copying:\n";
   for (vector<int>::const_iterator i = b.begin(); i != b.end(); ++i)
      cout << *i << endl;
}

【讨论】:

  • 我会重命名那些 unique_unorderedunique_unordered_inplace。 :)
  • 如果您要维护到目前为止看到的项目向量,不妨使用std::set 或(在C++0x 中)std::unordered_set。另外,请注意erase 对于std::vectorstd::deque 的速度很慢(O(n),使得整体算法为 O(n^2));相反,您可以一次只复制一个元素,方法是维护两个指向向量的指针(一个指向正在读取的项目,一个指向要写入的下一个空槽),然后将最后的向量截断为新的项目计数。或者只是复制到一个新的向量。
  • 确认。另请参阅 Scott Meyers 的 Efficient STL 书,该书详细介绍了该主题。仍有优化的余地,我只是想指出基本思想。
【解决方案3】:

从数组中删除重复项

这在技术上是不可能的,因为数组不能改变大小。

【讨论】:

    猜你喜欢
    • 2016-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    相关资源
    最近更新 更多