仅靠“小于运算“生存的map

对于初学STL的人来说,map可能是最全能的伙伴,只需敲下三个字母,一个天然有序,方便扩展,高效检索的数据结构就这样轻轻松松的产生了,这得益于C++的模版技术。在享受标准模版库带给我们便利的同时,我们也需要简单理解一下其内部的实现的某些关键机制,否则的话,就很可能就会犯下面这个的错误。

#include <map>
#include <string>
#include <iostream>
using namespace std;

class CKey
{
public:
    CKey(const int keyValue, const string keyName)
        : m_KeyValue(keyValue), m_KeyName(keyName)
    {
    }

    bool operator < (const CKey &key) const
    {
        return m_KeyValue < key.m_KeyValue;
    }

    bool operator == (const CKey &key) const
    {
        return m_KeyName == key.m_KeyName;
    }

private:
    int m_KeyValue;
    string m_KeyName;
};

class CValue
{
};


int main()
{
    map<CKey, CValue> key2Value;

    CKey key(0, "Alpha");
    CValue value;
    key2Value.insert(make_pair(key, value));

    CKey objKey(1, "Alpha");
    auto it = key2Value.find(objKey); //顺便隆重推荐下C++11中的新特性,超爽的auto!
    cout << (it == key2Value.end());
}

上面的代码输出什么呢,答案是1,意味着objKey在key2Value中查找不到,虽然CKey重载的==运算符是以m_KeyName为判断依据,而objKey和key拥有相同的m_keyName,但是map似乎根本不领情。

真相就是,对于map而言,它只关心<运算符,所以上面CKey中重载的==运算符对于map而言是没有任何作用的。

接下来的问题就是,没有==运算,map怎么知道自己找到了符合要求的那个元素呢?

其实,map虽然不知道等于的定义,但是它通过小于符号,知道了另外一种等价的定义,如果!(a < b) && !(b < a),那么a和b可以视为相等。

map的本质是一棵用key组建起来的红黑树(是一种对于每个树节点,左边元素全部小于父节点,右边元素全部大于父节点的平衡二叉树),find函数从map的根节点出发,如果根节点比要查找的F小,则移动到根节点的右子节点,否则移动到key节点的左子节点,再将移动后的节点视为根节点,重复上面的操作,直到遍历到某个节点没有需要的子节点为止。在遍历的过程中,关键的一点在于更新最后一个不小于F的树节点M(M初始为map的end())。最后比较F < M是否成立,如果成立,说明map中不存在要查找的元素F,否则,说明找到了需要查找的元素,就是记录到的M,因为满足了!(M < F) && !(F < M)。

                                                            10

                                                             /\

                                                         5      15

                                                        /\        /\

                                                      3   6   12  17

例如要从上面的树中查找15这个值。初始M=end,首先10 < 15,遍历到15;15 !< 15,更新M=15,遍历到12;12 <15,寻找12的右子节点,为空,遍历结束。记录到的节点M=15,15 < M不成立,所以认为查找到了。

若要查找11这个值,同样初始M=end,首先10 < 11,遍历到15;15 !< 11,更新M=15,遍历到12;12!<11,更新M=12,寻找12的左子节点,为空,遍历结束。记录到的节点是M=12,11 < M成立,所以认为查找失败了。

具体情况推荐大家去看STL的源码,上面都是根据VC++ STL源码得到的。

原文地址:https://www.cnblogs.com/itZhy/p/2710866.html