zoukankan      html  css  js  c++  java
  • 仅靠“小于运算“生存的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源码得到的。

  • 相关阅读:
    棋盘问题 简单搜索DFS
    Equivalent Strings
    素数环
    LeetCode Maximum Product Subarray
    UVA 725 – Division
    矩形嵌套问题
    ACM最大子串和问题
    ACM装箱问题
    ACM田胫赛马
    ACM雷达安放问题
  • 原文地址:https://www.cnblogs.com/itZhy/p/2710866.html
Copyright © 2011-2022 走看看