【C++】map与set的封装实现

发布于:2024-04-10 ⋅ 阅读:(179) ⋅ 点赞:(0)

目录

一,set和map结构框架

二,红黑树结构设计

2-1,红黑树的结点封装

2-2,红黑树的迭代器封装

2-2,红黑树的封装

三,set与map的封装


一,set和map结构框架

        map与set都是关联式容器,底层都是用红黑树结构实现,而map与set实际上只是套了一层膜,实际功能都是依靠红黑树结构来实现,包括迭代器。

        map容器中的元素类型可看成键值对pair型,set容器中的元素可只看成key类型。在底层结构中这里的传入结构对应是set<K> -> rb_tree<K, K>         map<K, V> -> rb_tree<K, pair<const K, V>>,其中rb_tree对应就是红黑树结构,其中rb_tree第二个模板参数对应的是set或map数据类型(其它复杂的这里不做考虑,我们只需实现基础功能——插入,明白具体的逻辑即可)。

        这里需注意的是,由于红黑树也是二叉搜索树,插入数据时需要进行比较,而map容器的顺序是只依靠key进行比较的,map中存储数据的数据数据类型pair的比较不是只依靠key比较,所以这里还需自定义比较,即向红黑树中插入数据时,只按照key的比较进行插入。

set结构封装

template<class K>
class set
{
    struct Oft
    {
        const K& operator()(const K& data)
        {
            return data; //set只有一个数据,只传入本身的数据
        }
    };
    //..........这里是实现封装的接口
private:
    RBTree<K, const K, Oft> _s;  //红黑树结构
};

map结构封装

template <class K, class V>
class map
{
    struct Oft
    {
        const K& operator()(const pair<K, V>& data)
        {
            return data.first;  //map数据是键值对,只传入first
        }
    };
    //..........这里是实现封装的接口
private:
    RBTree<K, pair<const K, V>, Oft> _m;  //红黑树结构
};

        再次强调一点,map和set只是封装了红黑树结构以及功能,具体实现都在红黑树结构中,所以当创建一个map或set容器,实际创建的是一颗红黑树,所以map和set存储数据的成员_s和_m的类型是红黑树,又因为set容器中数据不能修改以及map中key不可修改,value可以修改,所以这里需要用const进行部分修饰。以上类Oft传入红黑树中只用于后面红黑树插入时按照具体数据比较,具体使用请看下面红黑树结构。


二,红黑树结构设计

        红黑树的具体封装之前已详细讲解过了,这里实现set和map只需修改部分即可,不再重点说明红黑树的知识,若有不明白的请观察之前博客:http://t.csdnimg.cn/sXHKp

2-1,红黑树的结点封装

        结点用于存储数据,具体设计结构如下:

template <class T>
struct RBTreeNode
{
    RBTreeNode<T>* _parent;
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    Color _color;
    T _data;

    RBTreeNode(const T& data = T())
        : _parent(nullptr)
        , _left(nullptr)
        , _right(nullptr)
        , _color(RED)
        , _data(data)
    {    }
};

2-2,红黑树的迭代器封装

        迭代器的封装这里实现const_iterator和iterator两种类型。迭代器的常用功能有 *,->,==,!=,++,--,这里只对这些功能进行封装实现。

        由于const_iterator只是限制对容器里面的数据进行修改,其它功能与iterator相同,所以这里可直接使用模板来传递const型数据或非const型数据即可。

        红黑树迭代器++和--是实现的是中序遍历的下一个结点和上一个结点。

//Ptr要么是const T&(const_iterator),要么是T&(iterator)
//Ref要么是const T*(const_iterator),要么是T*(iterator)
//这两种具体类型是根据使用的是const_iterator还是iterator

template <class T, class Ptr, class Ref> 
struct RBTreeIterator
{
private:
    typedef RBTreeNode<T> Node;
    typedef RBTreeIterator<T, Ptr, Ref> Self;
public:
    Node* _node;


    RBTreeIterator(Node* node = nullptr)
        :_node(node)
    {    }


    Self& operator++() //前置++。中序的下一个结点
    {
        assert(_node);
        Node* cur = _node;
        //1,右子树存在,找右子树的最左结点
        if (_node->_right != nullptr)
        {
            cur = _node->_right;
            while (cur->_left)
            {
                cur = cur->_left;
            }
        }
        //2,右子树为空,逐渐往上层找cur是parent的左孩子
        else
        {
            Node* parent = cur->_parent;
            while (parent && cur != parent->_left)
            {
                cur = parent;
                parent = cur->_parent;
            }
            cur = parent;
        }
        _node = cur;
        return *this;
    }
    Self operator++(int) //后置++。中序的下一个结点
    {
        assert(_node);
        Self s(*this); //注意: 这里只是实现返回原本迭代器,浅拷贝实现即可
        Node* cur = _node;
        if (_node->_right != nullptr)
        {
            cur = _node->_right;
            while (cur->_left) cur = cur->_left;
        }
        else
        {
            Node* parent = cur->_parent;
            while (parent && cur != parent->_left)
            {
                cur = parent;
                parent = cur->_parent;
            }
            cur = parent;
        }
        _node = cur;
        return s;
    }
    Self& operator--() //前置--。中序的上一个结点
    {
        assert(_node);
        Node* cur = _node;
        //1,左子树存在,找左子树的最右结点
        if (_node->_left)
        {
            cur = _node->_left;
            while (cur->_right)
            {
                cur = cur->_right;
            }
        }
        //2,左子树为空,逐渐往上层找cur是parent的右孩子
        else
        {
            Node* parent = cur->_parent;
            while (parent && cur != parent->_right)
            {
                cur = parent;
                parent = cur->_parent;
            }
            cur = parent;
        }
        _node = cur;
        return *this;
    }
    Self operator--(int) //后置--。中序的上一个结点
    {
        assert(_node);
        Self s(*this);
        Node* cur = _node;
        if (_node->_left)
        {
            cur = _node->_left;
            while (cur->_right) cur = cur->_right;
        }
        else
        {
            Node* parent = cur->_parent;
            while (parent && cur != parent->_right)
            {
                cur = parent;
                parent = cur->_parent;
            }
            cur = parent;
        }
        _node = cur;
        return s;
    }

    Ptr operator*()
    {
        return _node->_data;
    }
    Ref operator->() //实现的是“->”符号的左边是右边的地址
    {
        return &_node->_data;
    }

    bool operator==(const Self& it)
    {
        return _node == it._node;
    }
    bool operator!=(const Self& it)
    {
        return _node != it._node;
    }
};

2-2,红黑树的封装

        这里我们封装插入算法和查找算法。其中插入算法中,由于旋转算法只针对于结点,所以这里的旋转跟红黑树结构里面的旋转一样。但插入数据和返回值需要做出调整。

        当插入数据进行数据结点数据间的比较时,这里需要根据以上传入的Oft类型进行比较(set按自己的数据比较,map只按照key比较)。若使用红黑树版本的比较算法,set由于只存储了key数据,所以对此无影响,但是map存储的是键值对,底层键值对的比较在key相同下会比较value,这里不符合预期。

        map和set底层结构插入算法的返回值都是pair<iterator, bool>,这里的返回值需做出调整。

        查找算法这里只对key进行查找。

template <class K, class T, class Oft>
class RBTree
{
    typedef RBTreeNode<T> Node;
public:
    typedef RBTreeIterator<T, T&, T*> iterator;
    typedef RBTreeIterator<T, const T&, const T*> const_iterator;

    RBTree() : _root(nullptr) {    }


    iterator begin()    //左子树的最左孩子
    {
        assert(_root);
        Node* cur = _root;
        while (cur->_left)
        {
            cur = cur->_left;
        }
        return cur;
    }
    iterator end()  //右子树的最右孩子的下一个位置,即空
    {
        return iterator(nullptr);
    }
    const_iterator begin() const
    {
        return begin();
    }
    const_iterator end() const
    {
        return end();
    }

    //查找。若是map,只对key进行查找

    iterator Find(const K& data)
    {
        assert(_root);
        Oft kv;
        Node* cur = _root;
        while (cur)
        {
            if (kv(cur->_data) < data)
            {
                cur = cur->_right;
            }
            else if (kv(cur->_data) > data)
            {
                cur = cur->_left;
            }
            else
                return cur;
        }
        return nullptr;
    }


    //make_pair返回值是pair,这里只需返回make_pair(插入结点迭代器,true或false)
    pair<iterator, bool> Insert(const T& data)  //插入
    {
        if (_root == nullptr) {
            _root = new Node(data);
            _root->_color = BLACK;
            return make_pair(iterator(_root), true);  
        }
        Node* cur = _root;
        Oft kv;
        Node* parent = _root->_parent;
        while (cur) {
            if (kv(cur->_data) > kv(data)) {
                parent = cur;
                cur = cur->_left;
            }
            else if (kv(cur->_data) < kv(data)) {
                parent = cur;
                cur = cur->_right;
            }
            else return make_pair(iterator(cur), false);
        }
        if (kv(parent->_data) > kv(data)) {
            parent->_left = new Node(data);
            cur = parent->_left;
        }
        else {
            parent->_right = new Node(data);
            cur = parent->_right;
        }
        cur->_parent = parent;
        Node* node = cur; //注意:下面的新插入结点cur会指向其它结点,这里需保存一下
        while (parent && parent->_color == RED) {
            Node* grandparent = parent->_parent;
            if (parent == grandparent->_left) {
                Node* uncle = grandparent->_right;
                if (uncle && uncle->_color == RED) {
                    grandparent->_color = RED;
                    parent->_color = uncle->_color = BLACK;
                    cur = grandparent;
                    parent = cur->_parent;
                }
                else {
                    if (cur == parent->_left) {
                        RotateR(grandparent);
                        grandparent->_color = RED;
                        parent->_color = BLACK;
                    }
                    else {
                        RotateLR(grandparent);
                        grandparent->_color = RED;
                        cur->_color = BLACK;
                    }
                    break;
                }
            }
            else {
                Node* uncle = grandparent->_left;
                if (uncle && uncle->_color == RED) {
                    grandparent->_color = RED;
                    parent->_color = uncle->_color = BLACK;
                    cur = grandparent;
                    parent = cur->_parent;
                }
                else {
                    if (cur == parent->_left) {
                        RotateRL(grandparent);
                        grandparent->_color = RED;
                        cur->_color = BLACK;
                    }
                    else {
                        RotateL(grandparent);
                        grandparent->_color = RED;
                        parent->_color = BLACK;
                    }
                    break;
                }
            }
        }
        _root->_color = BLACK;
        return make_pair(iterator(node), true);
    }

    //旋转算法
    void RotateL(Node* parent) //左单旋
    {
        Node* cur = parent->_right;
        Node* curL = cur->_left;
        Node* pparent = parent->_parent;
        parent->_right = curL;
        if (curL) curL->_parent = parent;
        if (parent == _root) {
            _root = cur;
            _root->_parent = nullptr;
        }
        else {
            if (pparent->_left == parent) {
                pparent->_left = cur;
                cur->_parent = pparent;
            }
            else {
                pparent->_right = cur;
                cur->_parent = pparent;
            }
        }
        cur->_left = parent;
        parent->_parent = cur;
    }
    void RotateR(Node* parent) //右单旋
    {
        Node* cur = parent->_left;
        Node* curR = cur->_right;
        Node* pparent = parent->_parent;
        parent->_left = curR;
        if (curR) curR->_parent = parent;
        if (parent == _root) {
            _root = cur;
            _root->_parent = nullptr;
        }
        else {
            if (pparent->_left == parent) {
                pparent->_left = cur;
                cur->_parent = pparent;
            }
            else {
                pparent->_right = cur;
                cur->_parent = pparent;
            }
        }
        cur->_right = parent;
        parent->_parent = cur;
    }
    void RotateLR(Node* parent) //左右旋
    {
        RotateL(parent->_left);
        RotateR(parent);
    }
    void RotateRL(Node* parent) //右左旋
    {
        RotateR(parent->_right);
        RotateL(parent);
    }
private:
    Node* _root;
};


三,set与map的封装

        set与map的接口总封装只需调用红黑树RBTree相应的接口即可,可以说这里只是套了一层膜。这里重点说明map容器对“ [] ”的重载,即插入key和value,返回value,若不存在value时将默认构造。

//set封装

template<class K>
class set
{
    struct Oft
    {
        const K& operator()(const K& data)
        {
            return data;
        }
    };
public:
    typedef RBTreeNode<const K> Node;
    typedef typename RBTree<K, const K, Oft>::iterator iterator;
    typedef typename RBTree<K, const K, Oft>::const_iterator const_iterator;

    iterator begin()
    {
        return _s.begin();
    }
    iterator end()
    {
        return _s.end();
    }
    const_iterator begin() const
    {
        return _s.begin();
    }
    const_iterator end() const
    {
        return _s.end();
    }

    iterator find(const K& k)
    {
        return _s.Find(k);
    }

    pair<iterator, bool> insert(const K& data)
    {
        return _s.Insert(data);
    }
private:
    RBTree<K, const K, Oft> _s;
};

//map封装
template <class K, class V>
class map
{
    struct Oft
    {
        const K& operator()(const pair<K, V>& data)
        {
            return data.first;
        }
    };
public:
    typedef RBTreeNode<pair<const K, V>> Node;
    typedef typename RBTree<K, pair<const K, V>, Oft>::iterator iterator;
    typedef typename RBTree<K, pair<const K, V>, Oft>::const_iterator const_iterator;

    iterator begin()
    {
        return _m.begin();
    }
    iterator end()
    {
        return _m.end();
    }
    const_iterator begin() const
    {
        return _m.begin();
    }
    const_iterator end() const
    {
        return _m.end();
    }

    pair<iterator, bool> insert(const pair<K, V>& data)
    {
        return _m.Insert(data);
    }

    iterator find(const K& data)
    {
        return _m.Find(data);
    }

    V& operator[](const K& key) //这里需注意value是否存在
    {
        _m.Insert(make_pair(key, V()));
        iterator it = find(key);
        return it->second;
    }
private:
    RBTree<K, pair<const K, V>, Oft> _m;
};

        下面我们认识一下typename关键字。在模板编程中,当模板参数是一个类型,而你又想在模板内部使用这个类型来定义另一个类型时,你需要使用 typename 关键字来告诉编译器这个标识符是一个类型名,而不是一个静态成员变量。如:上面typedef typename RBTree<K, pair<const K, V>, Oft>::iterator iterator; 因为编译器在解析模板时不知道 RBTree<K, pair<const K, V>, Oft> 的确切类型,所以不确定 RBTree<K, pair<const K, V>, Oft>::iterator iterator 是一个类型还是一个静态成员。使用 typename 关键字告诉编译器这是一个类型。总结来说,在模板编程中,当你定义的类型别名依赖于模板参数,并且这个别名是一个类型时,你需要使用 typename 关键字。在其他情况下,使用 typedef 定义类型别名时不需要 typename