【问题标题】:How do I check if two std::vector's contain only the same elements?如何检查两个 std::vectors 是否仅包含相同的元素?
【发布时间】:2012-05-17 09:47:22
【问题描述】:

我需要一个算法或标准库函数来比较两个向量元素,如下所示:

class Utility
{
    template <class T>
    static bool CheckIfVectorsEquivalent(   const std::vector<T> & Vec1,
                                            const std::vector<T> & Vec2)
    {
        // ???
    }
};

按照以下规范工作:

std::vector<int> v1, v2, v3, v4, v5, v6, v7, v8;

// Returns false when not all the elements are matching between vectors
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v2.push_back(2);
v2.push_back(3);
v2.push_back(8);
Utility::CheckIfVectorsEquivalent(v1, v2);  // Must return false

// Returns true when all the elements match, even if the are not in the same order
v3.push_back(3);
v3.push_back(1);
v3.push_back(7);
v4.push_back(7);
v4.push_back(3);
v4.push_back(1);
Utility::CheckIfVectorsEquivalent(v3, v4);  // Must return true

// Returns false when one of the vectors is subset of the other one
v5.push_back(3);
v5.push_back(1);
v5.push_back(7);
v6.push_back(7);
v6.push_back(3);
v6.push_back(1);
v6.push_back(18);
v6.push_back(51);
Utility::CheckIfVectorsEquivalent(v5, v6);  // Must return false

// Returns true when the both vectors are empty
Utility::CheckIfVectorsEquivalent(v7, v8);  // Must return true

是否有任何标准(使用 STL)方法可以做到这一点?如果没有,我该如何编写这个算法?这让我很困惑。

【问题讨论】:

    标签: c++ algorithm stl vector comparison


    【解决方案1】:

    标准方法是对这两个向量进行排序并使用运算符==,比较相应的值。

    实现该算法的示例方案为:

    #include <vector>
    #include <algorithm>
    
    template<typename T>
    bool compare(std::vector<T>& v1, std::vector<T>& v2)
    {
        std::sort(v1.begin(), v1.end());
        std::sort(v2.begin(), v2.end());
        return v1 == v2;
    }
    

    由于排序,其复杂度为 O(n*log(n))。

    【讨论】:

    • 对非常小的集合进行 O(n^2) 比较可能会更快,但仅适用于小集合。这个答案在 99% 的时间里都是准确的。
    • “这也是最快的可能算法”证明?
    • 不使用集合或其他类似的东西,而是简单地对向量进行排序来自我的经验。从复杂性的角度来看,这是 O(n log n)。这意味着,您必须(没有任何其他假设)将n! 不同的向量识别为相等,并且该决策树的大小为log(n!) = Omega(n log n)。更精确的证明可以类似于排序下界。
    • @AlanStokes:并且有 O(N) 个最佳案例排序。正如我所说,“尽可能快”并不是保证,应该删除 IMO。
    • @RafałRawicki:你是不是被欺负了把一个错误的答案改成了另一个更糟糕的答案?您的回答仍然只对订单有意义,但现在您已经删除了关于这种情况下复杂性的有趣(且正确)评论。
    【解决方案2】:

    如果您可以只使用 c++11 解决方案,那么 std::is_permutation 正是您想要的

    template <class FI1, class FI2>
    bool is_permutation ( FI1 first, FI1 last, FI2 d_first );
    

    如果你做不到,那么在即将发布的 boost 1.50 版本中,将会有

    boost::algorithm::is_permutation
    

    具有相同的界面。

    【讨论】:

    • 标准说,std::is_permutation 最多比较元素 O(n^2),所以这可能比较慢,但写起来很短。
    • 我认为这是最好的答案,因为它不对类型 T 做出顺序假设。
    • 在最坏的情况下肯定是 O(N^2),但它不使用额外的内存。
    【解决方案3】:

    从每个向量创建一个multiset,然后比较它们是否相等。

    【讨论】:

      【解决方案4】:

      对于未排序的输入,这个问题有一个 O(n) 的解决方案:

      #include <vector>
      #include <algorithm>
      #include <iostream>
      #include <boost/function_output_iterator.hpp>
      
      template <typename T>
      T xorfunc(const T& a, const T& b) {
        return a^b;
      }
      
      template <typename T>
      bool compare(const std::vector<T>& v1, const std::vector<T>& v2) {
        if (v1.size() != v2.size())
          return false;
      
        T result = 0;
        std::transform(v1.begin(), v1.end(), v2.begin(), boost::make_function_output_iterator([&result](const T& r) { result ^= r; }), std::ptr_fun(&xorfunc<T>));
        return !result;
      }
      

      适用于整数输入,并利用a ^ b ^ c ^ d == 0 用于配对值的任何组合这一事实。它永远不会给出误报,它可能可能会给出误报,但您可以在 O(n) 空间/时间内进一步减少这些误报。如果您主要是打负数,那么这可能作为预排序+比较步骤来快速排除它们很有用。它适用于您展示的所有测试用例:

      int main() {
          std::vector<int> v1, v2, v3, v4, v5, v6, v7, v8;
      
          // Returns false when not all the elements are matching between vectors
          v1.push_back(1);
          v1.push_back(3);
          v1.push_back(5);
          v2.push_back(2);
          v2.push_back(3);
          v2.push_back(8);
          std::cout << compare(v1, v2) << " (false)" << std::endl;  // Must return false
      
          // Returns true when all the elements match, even if the are not in the same order
          v3.push_back(3);
          v3.push_back(1);
          v3.push_back(7);
          v4.push_back(7);
          v4.push_back(3);
          v4.push_back(1);
          std::cout << compare(v3, v4) << " (true)" << std::endl;  // Must return true
      
          // Returns false when one of the vectors is subset of the other one
          v5.push_back(3);
          v5.push_back(1);
          v5.push_back(7);
          v6.push_back(7);
          v6.push_back(3);
          v6.push_back(1);
          v6.push_back(18);
          v6.push_back(51);
          std::cout << compare(v5, v6) << " false" << std::endl;  // Must return false
      
          // Returns true when the both vectors are empty
          std::cout << compare(v7, v8) << " true" << std::endl;  // Must return true
      
      }
      

      给予:

      0(假) 1(真实) 0 假 1 对

      【讨论】:

      • 如果它们已经排序,你可以在向量上使用operator==
      • mismatch 对排序后的直接相等性有何改进?
      • 它给你的迭代器告诉你差异在哪里,可能/可能不感兴趣
      • @awoodland 有些情况下 this 会错误地返回 true。
      • @awoodland:快速浏览一下,您希望对所有元素进行异或运算,并期望在它们都相等时得到零,对吧?这是问题所在:Vec1[0, 12]; Vec2[4, 8].
      【解决方案5】:

      最简单的方法是创建 Vec1 和 Vec2 的副本,对它们进行排序并使用 == 进行比较。

      另一种方法是从 Vec1 和 Vec2 构建两个多重集,并使用 == 比较它们。

      另一种方法是使用映射(std::map 或 unordered_map)来存储计数器 - 即增加 Vec1 的每个元素的存储值并减少 Vec2 的每个存储元素,然后检查 map 是否包含非零元素。如果map中存储了非零元素,则向量不相等。

      凌乱的伪代码示例:

      std::vector<Value> vec1, vec2;
      //initialize vec1 and vec2, fill with data
      typedef int Counter;
      typedef unordered_map<Value, Counter> CounterMap;
      
      CounterMap counters;
      for (size_t i = 0; i < vec1.size(); i++)
          counters[vec1[i]]++;
      for (size_t i = 0; i < vec2.size(); i++)
          counters[vec2[i]]--;
      
      bool equal = true;
      for (CounterMap::const_iterator i = coutners.begin(); equal && (i != counters.end()); i++)
          if (i->second != 0)
              equal = false;
      

      根据您的 STL(或 boost)实现、数据类型和向量中存储数据的顺序)其中一种方法会更快,但很难区分是哪一种。

      【讨论】:

      • 无序多重集的相等比较是最坏情况 O(N^2),所以可能值得避免。
      【解决方案6】:

      如果我们认为 int 足够大,可以在以下算法中完成计算,则有一个简单的 O(N) 算法。

      0:初始化:max(v1,v2)的大小有素数,product1=1,product2=1

      1:如果 v1 的大小 != v2 的大小,则返回 false

      2:

      foreach (  i in v1 ) {
         product1 *= primes[v1[i]]
         product2 *= primes[v2[i]]
      }
      

      3.

      result product1 == product2;
      

      【讨论】:

        【解决方案7】:
          v7.push_back(3);
          v7.push_back(1);
          v7.push_back(7);
        
          v8.push_back(7);
          v8.push_back(3);
          v8.push_back(1);
          v8.push_back(1);
          v8.push_back(7);
        
          std::cout << compare(v7, v8) << " true or false" << std::endl;  
        

        条件如上,函数比较会返回真或假。我记错了。 如果返回真。 我们不能使用这种方式: 标准方法是对这两个向量进行排序并使用运算符 == 来比较相应的值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-08-21
          • 1970-01-01
          • 2019-08-24
          • 1970-01-01
          • 2020-11-19
          • 2023-03-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多