set::find()找不到集合中的键

keys in the set cannot be found by set::find()

本文关键字:集合 find set 找不到      更新时间:2023-10-16

我在用set做一些实验时遇到了这个问题。

我使用一个以2个整数为密钥的结构:

struct Key {
    int k1;
    int k2;
};

并使用一个类来构建密钥之间的排序:

struct keyComp {
    bool operator () (const struct Key& lhs, const struct Key& rhs) const {
        if (lhs.k1 < rhs.k1)
            return true;
        else if (lhs.k2 < rhs.k2)
            return true;
        else
            return false;
    }
};

但有了这个比较器,该集就没能找到一些现有的密钥。例如,在这个程序中,我在集合中存储了9个密钥,从(0, 0)(2, 2):

Key pos;
set <Key, keyComp> aset;
// insert elements from (0, 0) to (2, 2)
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        pos.k1 = i;
        pos.k2 = j;
        aset.insert (pos);
    }
}
// now try to find each of them
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        pos.k1 = i;
        pos.k2 = j;
        set <Key, keyComp> :: iterator it = aset.find (pos);
        if (it != aset.end ())
            cout << "v "; // element found
        else
            cout << "! "; // not found
    }
    cout << endl;
}
// print the set
cout << "element in set : ";
for (set <Key, keyComp> :: iterator it = aset.begin (); it != aset.end (); it++) {
    cout << "(" << it->k1 << ", " << it->k2 << ") ";
}
cout << endl;

我预计它会打印9个v,这意味着所有的密钥都找到了。但我得到了:

v v v 
! ! v 
! ! v 
element in set : (0, 0) (1, 0) (2, 0) (0, 1) (1, 1) (2, 1) (0, 2) (1, 2) (2, 2)

有些钥匙可以找到,但有些却找不到,甚至它们都在套装中。

此外,如果我将比较器更改为:

struct keyComp {
    bool operator () (const struct Key& lhs, const struct Key& rhs) const {
        // combine the two keys for comparison
        // say LARGE_NUMBER is a number bigger than all k2
        return lhs.k1 * LARGE_NUMBER + lhs.k2 < rhs.k1 * LARGE_NUMBER + rhs.k2;
    }
};

然后找到所有钥匙。

为什么会发生这种情况?是因为原始比较器未能构造键之间的顺序吗?

您的比较器没有提供正确的排序,这意味着set的内部在试图找出插入或查找内容的位置时会做各种奇怪的事情(也称为"未定义行为")。

你需要这样的东西:

    if (lhs.k1 < rhs.k1)
        return true;
    if (lhs.k1 > rhs.k1)
        return false;
    return (lhs.k2 < rhs.k2);

您的比较器没有定义严格的弱排序。(例如,在比较器中,(2,1)(1,2)在两个方向上给出true。)您需要类似字典排序的东西:

return (lhs.k1 < rhs.k1)  || ( !(rhs.k1 < lhs.k1) && (lhs.k2 < rhs.k2) );