【问题标题】:How to compare two vectors for equality?如何比较两个向量是否相等?
【发布时间】:2021-05-23 21:04:03
【问题描述】:

我有以下程序:

std::vector<int> nums = {1, 2, 3, 4, 5};
std::vector<int> nums2 = {5, 4, 3, 2, 1};

bool equal = std::equal(nums.begin(), nums.end(), nums2.begin());
if (equal)
{
    cout << "Both vectors are equal" << endl;
}

有两个元素相等的向量。 std::equal 函数在这里不起作用,因为它按顺序执行并比较相应的元素。有没有一种方法可以检查这两个向量是否相等并且在我的情况下是否为真而无需排序?在实际示例中,我没有整数,而是自定义对象,它们比较为指针的相等性。

【问题讨论】:

  • 要么对向量进行排序,要么使用std::unordered_set - 这些是选项吗?
  • 是的,这可以工作,谢谢,但也许有一些 stl 算法?
  • 如果有重复的元素,你会寻求什么结果。例如,根据您的标准,{1,2,3,4,4} 是否与 {4,3,2,1} 比较?
  • 感谢大家,真的很有帮助。实际上算法 is_permutation() 正是我正在寻找的。就我而言,性能并不重要。

标签: c++ algorithm stl


【解决方案1】:

你可以从每个向量构造一个std::unordered_set,然后比较它们,如下面的代码sn-p所示:

#include <iostream>  
#include <vector>  
#include <unordered_set> 

using namespace std;

int main()
{
    std::vector<int> nums = { 1, 2, 3, 4, 5 };
    std::vector<int> nums2 = { 5, 4, 3, 2, 1 };
    std::vector<int> nums3 = { 5, 4, 9, 2, 1 };

    std::unordered_set<int> s1(nums.begin(), nums.end());
    std::unordered_set<int> s2(nums2.begin(), nums2.end());
    std::unordered_set<int> s3(nums3.begin(), nums3.end());

    if (s1 == s2) {
        std::cout << "1 and 2 are equal";
    }
    else {
        std::cout << "1 and 2 are different";
    }
    std::cout << std::endl;

    if (s1 == s3) {
        std::cout << "1 and 3 are equal";
    }
    else {
        std::cout << "1 and 3 are different";
    }
    std::cout << std::endl;

    return 0;
}

但是,有几点需要牢记:

  1. 对于自定义类型对象的向量,您需要为该类型提供一个operator==(但无论如何都必须这样做,或者您怎么能说这两个 向量 是否具有相同的内容)。
  2. 包含重复条目的向量将创建删除这些重复项的集合:因此,{1, 2, 2, 3} 将显示为等于 {1, 2, 3}
  3. 您还需要为您的自定义类型提供std:hash。对于一个简单的类,bob,它只包装一个整数,该哈希和所需的operator==,可以定义如下;然后,您可以用&lt;bob&gt; 替换上面示例中的&lt;int&gt; 专业化,它会起作用。 (此cppreference article 解释了有关哈希的更多信息。)
class bob {
public:
    int data;
    bob(int arg) : data{ arg } { }
};
bool operator==(const bob& lhs, const bob& rhs)
{
    return lhs.data == rhs.data;
}
template<> struct std::hash<bob> {
    std::size_t operator()(bob const& b) const noexcept {
        return static_cast<size_t>(b.data);
    }
};

【讨论】:

  • 谢谢,是的,这正是我所需要的。
  • 您还需要一种方法来散列自定义对象。
  • std::unordered_multiset 不会删除重复项。
  • @Arty 是的 - 我看到了,但链接的 cppreference 页面还指出,std::unordered_multisetstd::equal_to 不可用;但是,一个operator==,但我不清楚它是如何工作的。
  • @Arty 我想 OP 可以添加一个检查两个向量是否具有相同的 .size() - 但这仍然会给{1, 2, 2, 3}{1, 2, 3, 3} 等情况留下问题。
【解决方案2】:

std::is_permutation 标准库算法完全符合您的要求。如果两个范围以任意顺序包含相同的元素,则返回 true

对于某些应用程序可能会很慢,但它只需要相等比较,不需要临时容器或额外的内存分配。

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

int main()
{
    std::vector<int> nums = { 1, 2, 3, 4, 5 };
    std::vector<int> nums2 = { 5, 4, 3, 2, 1 };

    bool equal = std::is_permutation(nums.begin(), nums.end(), nums2.begin(), nums2.end());

    if (equal) {
        std::cout << "Both vectors are equal" << std::endl;
    }
}

【讨论】:

    【解决方案3】:

    您正在寻找setmultiset。 C++ 标准库确实有这些数据结构的实现:

    【讨论】:

    • 为此,我必须为自定义对象重载运算符。这对我不起作用,因为我没有任何比较标准。有没有办法在不排序的情况下比较顺序对象?
    • @Oleg 使用unordered_setunordered_multiset。他们需要你专门化 std::hash 并重载相等运算符。
    • @Oleg 矢量确实不是那里的正确工具。这正是集合数据结构的域。特别是如果这些容器中有很多对象。
    【解决方案4】:

    如果我必须发明一种算法来从头开始执行此操作,因为无论出于何种原因使用集合或排序都不起作用,我会考虑删除匹配项。这将是低效的 - 它是 O(n^2) - 但它会工作,并且在许多情况下效率低下不太可能成为问题:

    bool compare(A, B)
      copy B to BB
      for each a in A
        if BB contains a
          remove a from BB
        else
          return false
    
      if BB is empty
        return true
    
      return false
    

    【讨论】:

    • 谢谢,是的,我只是想用一些 stl 算法来做这个,但这就是我的想法。
    【解决方案5】:

    使用std::sort 提供另一种解决任务的方法。如果向量具有重复元素,它也可以正常工作(如下例所示)。它也相当快,O(N*Log(N)) 平均时间,在现实生活中将接近使用 std::unordered_set 或 std::unordered_multiset 的解决方案。

    是的,我看到 OP 要求在没有排序的情况下实现,但实际上 std::is_permutation 也几乎可以肯定地在下面进行排序。 std::set 也使用树间接排序。甚至 std::unordered_set 也会按哈希值进行排序(哈希分桶是一种基数排序)。所以如果没有一种间接排序或其他类型的排序整个向量,这个任务可能无法解决。

    Try it online!

    #include <algorithm>
    #include <vector>
    #include <iostream>
    
    int main() {
        std::vector<int> nums1 = {1, 2, 3, 4, 1, 5}, nums2 = {5, 4, 3, 1, 2, 1};
        std::sort(nums1.begin(), nums1.end());
        std::sort(nums2.begin(), nums2.end());
        std::cout << std::boolalpha << (nums1 == nums2) << std::endl;
    }
    

    输出:

    true
    

    【讨论】:

    • std::is_permutation 不会修改数据,因此没有排序。这就是为什么它具有 O(n^2) 最坏情况复杂度的原因。这也是为什么我添加了关于速度的警告,但它确实有效,并且几乎可以肯定对于许多用途来说“足够快”。
    • @Blastfurnace 有趣的是它是如何完成任务的?据说它的复杂度从 O(N^2) 到 O(N)。有什么算法吗?标准是否也限制不同的编译器在 O(N^2) 时间内实现它?我没有在标准中看到它说明它应该如何实施。有些编译器不能只在临时内存中进行排序吗?
    • Cppreference 有一个possible implementation。也可以看看主要的库实现,这里是libstdc++,微软的实现starts here
    • 我认为算法可以追溯到 Alex Stepanov,有一些 info here in his note
    • 如果您对标准库算法的设计感兴趣,大部分功劳归功于 Alexander Stepanov,他写了几本关于泛型编程的好书。
    猜你喜欢
    • 2014-09-20
    • 2013-08-18
    • 1970-01-01
    • 2021-09-14
    • 2023-03-11
    • 2010-12-05
    • 2016-03-06
    • 1970-01-01
    相关资源
    最近更新 更多