【问题标题】:<set> with custom struct contains duplicates具有自定义结构的 <set> 包含重复项
【发布时间】:2016-12-02 15:21:30
【问题描述】:

我一直在学习 C++。我被这个问题困住了。

我有 set,其中包含一个自定义结构,该结构包含两个 long int 的 a 和 b。我有一个自定义比较器结构,它比较数字并在 a 或 b 不同时返回 true。

typedef long int li;

struct number {
    number(li a1,li b1): a(a1), b(b1) {}
    li a, b;
};

struct compare {
    bool operator() (const number &lhs, const number& rhs) const{
        return lhs.a != rhs.a || lhs.b != rhs.b;
    }
};

int main() {
    set<number, compare> nums;
    nums.insert(number(1, 2));
    nums.insert(number(1, 1));
    nums.insert(number(2, 1));
    nums.insert(number(1, 2));
    for (auto &i : nums) {
        cout << i.a << " " << i.b << endl;
    }
    return 0;
}

这里的输出是

1 2

2 1

1 1

1 2

它有两个1 2条目。任何澄清将不胜感激。

【问题讨论】:

  • compare 不满足 the requirements of strict weak ordering,因此您的程序表现出未定义的行为。基本上,比较器应该类似于小于比较,而不是不相等。
  • 如果你想使用不等式而不是小于,你可以考虑std::unordered_set,它需要能够计算哈希并比较是否相等。
  • 感谢您的评论。有人可以解释为什么我必须使用小于一个而不是不平等?我还是有点困惑。
  • @TheAbsurd 考虑 7 和 9。79 为假。为了您的比较,它们都是正确的。您不知道 std::set 如何在幕后处理排序和检测重复项,但许多排序算法使用“
  • @RyanP 这是有道理的。非常感谢。

标签: c++ struct set comparator stdset


【解决方案1】:

您的比较函数应该返回某个元素是否小于另一个元素,而不是它们是否相等。 (更正式地说,它必须在你的集合元素上定义一个"Strict weak ordering"。)

使用类似的东西

struct compare {
    bool operator() (const number &lhs, const number& rhs) const{
        return std::tie(lhs.a, lhs.b) < std::tie(rhs.a, rhs.b);
    }
};

如果您不关心排序,您可能需要为您的类型定义一个合适的哈希函数并使用std::unordered_set

为避免将来出现此类问题,请务必阅读the docs。他们清楚地解释了您的comparison function 应该做什么。

供参考:上面使用的std::tie 构造了对其参数的引用元组,然后可以按字典顺序与&lt; 进行比较。这是一种简单、通用且快速的方法,可以为不具可比性的东西的集合建立一些排序。

【讨论】:

  • @TheAbsurd 链接到std::tie 文档:en.cppreference.com/w/cpp/utility/tuple/tie 如果您觉得这很奇怪,那么像其他答案中的简单if 构造对于像这样的简单情况也可以。跨度>
  • 呃..你的比较器必须定义一个严格的小于,而不是大于的弱排序。否则,您将设置一个集合以包含相反顺序的项目。 (您的代码正在这样做,但第一句话声称“大于”)
  • “否则,您将设置一个集合以包含相反顺序的项目。” 这取决于您想要的顺序(从技术上讲,我的第一句话是正确的,虽然可能有点误导)。 :) 编辑使其更清晰,谢谢。 @AndreKostur
【解决方案2】:

您的比较函数需要满足严格/弱排序要求。

(我实际上更喜欢使用std::tie 的答案,但这对新手来说可能更能说明问题)

bool compare(const number& lhs, const number& rhs)
{
   if(lhs.a < rhs.a)
      return true;
   else if(lhs.a > rhs.a)
      return false;
   else
      return lhs.b < rhs.b;
}

【讨论】:

  • 涉及return时不需要else
  • @MartinBonner 我认为完全相反,我认为如果没有其他一切,代码会更具可读性
  • 为什么不只是if(lhs.a == rhs.a) return lhs.b&lt;rhs.b; else return lhs.a&lt;rhs.a;
  • @MartinBonner 我喜欢std::tie,因为它真的很难搞砸。但是对于这种简单的情况,ifs 当然也可以。
  • @TheAbsurd 因为这在文档中作为要求声明
猜你喜欢
  • 2017-10-21
  • 1970-01-01
  • 2018-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-21
相关资源
最近更新 更多