map和set

本文详细介绍了STL中的set、map、multiset和multimap,包括它们的概念、使用方法、底层结构(红黑树)以及成员函数。特别讨论了map的[]运算符重载和自定义红黑树的实现,还提供了相关的测试代码以加深理解。
摘要由CSDN通过智能技术生成

目录

一、set

1.什么是set

2.set的使用

(1)默认成员函数

(2)迭代器

(3)容量操作

(4)操作函数

3.测试代码

二、map

1.键值对

2.什么是map

3.map的使用

三、multiset和multimap

四、map和set的封装

1.改造红黑树变量和类型

2.增加迭代器

3.修改成员函数

4.建立map和set的头文件

5.测试代码


一、set

1.什么是set

set就是STL中的普通搜索二叉树,它的底层结构是红黑树。

其中T表示储存元素的类型,Compare表示比较使用的仿函数,Alloc表示空间配置器,也就是我们每插入一个元素都需要它来开辟空间,绝大部分时候不需要我们传递。

2.set的使用

(1)默认成员函数

首先是构造函数和拷贝构造函数

explicit set (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); ———— 默认构造函数,可以构造一个空set

template

set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(),

const allocator_type& alloc = allocator_type()); ———— 按任意容器的迭代器区间构造

set (const set& x); ———— 拷贝构造

~set(); ———— 析构函数

set& operator= (const set& x); ———— 赋值运算符重载

(2)迭代器

iterator begin(); ———— 返回起始位置迭代器

const_iterator begin() const; ———— 返回起始位置const迭代器

iterator end(); ———— 返回末尾位置后一个的迭代器

const_iterator end() const; ———— 返回末尾位置后一个的const迭代器

reverse_iterator rbegin(); ———— 返回末尾位置反向迭代器

const_reverse_iterator rbegin() const; ———— 返回末尾位置反向const迭代器

reverse_iterator rend(); ———— 返回起始位置前一个的反向迭代器

const_reverse_iterator rend() const; ———— 返回起始位置前一个的反向const迭代器

再C++11中也支持了cbegin、cend、crbegin、crend返回const迭代器,对迭代器解引用得到value值,但是set中key和value值相同,所以最终间接得到key值。

(3)容量操作

bool empty() const; ———— 判断是否为空

size_t size() const; ———— 返回有效数据数

(4)操作函数

pair insert (const value_type& val); ———— 插入一个元素并返回一个键值对(键值对是一个类,有两个成员变量,在std命名空间中),插入成功则pair中的迭代器是当前节点,bool值为true,插入失败则迭代器为end(),bool值为false

template

void insert (InputIterator first, InputIterator last); ———— 插入任意容器迭代器区间的元素

size_t erase (const value_t& val); ———— 删除对应元素

void erase (iterator position); ———— 删除迭代器位置上的元素

void erase (iterator first, iterator last); ———— 删除set迭代器范围内的元素

void clear(); ———— 清空set

iterator find (const value_type& val) const; ———— 查找set中的元素

size_t count (const value_type& val) const; ———— 返回set中的val出现的次数,由于set中的内容不重复,所以它只能返回1和0

3.测试代码

#include<iostream>
#include<set>
using namespace std;
int main()
{
    set<int> s;
    s.insert(4);
    s.insert(7);
    s.insert(1);
    s.insert(15);
    s.insert(12);
    s.insert(18);
    s.insert(3);
 
    set<int>::iterator it = s.begin();
    for (auto e : s)
    {
        cout << *it << " ";
         ++it;
    }
    cout << endl;
 
    set<int>::iterator pos = s.find(15);
    cout << *pos << endl;
    s.erase(15);
    
    set<int>::iterator it1 = s.begin();
    while (it1 != s.end())
    {
        cout << *it1++ << " ";
    }
    cout << endl;
 
    cout << s.size() << endl;
    cout << s.max_size() << endl;
 
    s.clear();
 
    set<int>::iterator it2 = s.begin();
    while (it2 != s.end())
    {
    cout << *it2++ << " ";
    }
    cout << endl;
 
    set<int> s1(s);
    set<int>::iterator it3 = s1.begin();
    for (auto e : s1)
    {
        cout << *it3 << " ";
        it3++;
    }
    cout << endl;
    return 0;
}

二、map

1.键值对

键值对是用来表示具有一一对应关系的一种结构,一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。

在学习二叉搜索树时,我们通过储存值的大小决定插入位置在左还是右,这个存储的值就是键值key,如果我们将储存的元素变成一个键值对,参数就由改成了,修改前是子节点位置与值相关联,修改后就是key与value相关联,搜索树通过递归找到对应key值,同时内部储存的对应信息value也就被找到了,比如英译汉的词典中,你通过输入一个英文单词作为key值,搜索二叉树找到对应节点的键值对就找到了value,也就是汉语意思。

在set和map中都会涉及键值对pair,class Alloc = allocator<const Key, T>,这里的pair其实就是一个包含两个变量的结构体,前面的值叫first,后面的叫second,代表前后的模板参数,再std的命名空间中就存在这样一个数据结构。

需要注意:Key不可以被修改,T可以。

2.什么是map

map就是STL中的Key,Value结构的搜索二叉树,它的底层结构也是红黑树。它的每个元素是一个键值对,用户通过输入key,可以找到对应的value值。这里的key和value就不一样了,map就可以实现类似英汉字典的功能了。

与map类似,但是这里的空间配置器已经直接出现了pair,也出现了需要传递的key和value的类型Key和T

3.map的使用

map和set的成员函数可实现的功能基本一致,迭代器解引用返回的是key对应节点出差呢的键值对pair,不过map重载了[]运算符,我们重点讲讲这个函数。

mapped_type& operator[] (const key_type& k); ———— []运算符重载

先看看它的实现:

mapped_type& operator[] (const key_type& k)
{
    return (*((this->insert(make_pair(k,mapped_type()))).first)).second
}

 你一定想说,这都什么呀这是?

我知道你急,但是你先别急,请抑制住你砸电脑的心情,至少我们看到了insert函数,那就先从insert函数开始。

pair insert (const value_type& val); ———— 插入一个元素【这个元素可使用std命名空间中的make_pair(key, value)函数进行构造】并返回pair,插入成功则返回的pair的迭代器指向插入位置,bool值为true,插入失败则pair的迭代器为end(),bool值为false

这样就好理解了,(*((   this->insert(make_pair(k,mapped_type()))   ).first)).second中:this->insert(make_pair(k,mapped_type())) 的部分返回一个pair变量,我们把这个pair命名为p1,现在就是:(*((p1).first)).second

如果insert成功插入,p的first是插入位置的迭代器,将它命名为i,得到:(*(i)).second,迭代器解引用得到pair,命名这个pair为p2得到:p2.second,此时我们就理解了最后返回的就是map节点储存的value值。

如果insert插入失败,p的first是map中已经存在的这个位置的迭代器,应用同样的办法也返回了该节点储存的value值。

总结来说,如果向operator []中传递一个map中不存在的key,那么它就为我们插入一个key,value的pair,value是默认构造,而如果后面有赋值则直接赋值到value上;如果传递一个已经存在的元素,他就会给我们返回对应的value。

因为它有插入数据的特性,所以对这个函数的使用请务必小心。

测试代码:

#include<iostream>
#include<map>
#include<set>

using namespace std;

int main()
{
    map<int, char> m;
    m.insert(make_pair(1, 'a'));
    m.insert(make_pair(2, 'b'));
    m.insert(make_pair(3, 'c'));
    m.insert(make_pair(4, 'd'));

    cout << m[1] << endl;
    cout << m[5] << endl;
    
    for (auto e : m)
    {
        cout << e.first << "->" << e.second << endl;
    }
    
    return 0;
}

三、multiset和multimap

multiset和multimap的底层实现也是红黑树,相比普通的set和map最大的区别就是它的元素是可以重复的,对于它的在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。

因为一个key对应多个value,所以multimap中不存在[]重载。

测试代码:

#include<iostream>
#include<map>
#include<set>

using namespace std;

int main()
{
    multimap<string, string> m;
    m.insert(make_pair("poor", "可怜的"));
    m.insert(make_pair("poor", "贫穷的"));
    m.insert(make_pair("argue", "争论"));
    m.insert(make_pair("argue", "论证"));
    m.insert(make_pair("argue", "声称"));
    
    for (auto e : m)
    {
        cout << e.first << "->" << e.second << endl;
    }
    
    return 0;
}

运行结果:

证明multimap可以建立一个key对应多个value的映射。

四、map和set的封装

1.改造红黑树变量和类型

我们之前就已经写过红黑树,现在我们需要把他改变为更普适性的版本。

我们原来插入的元素都是pair,把它改为T,成员变量_kv改为_data。

当然,下面的class RBTree中的内容也要改,value的类型改为T,加了一个KeyOf用于根据T找到它对应的Key(你只储存data,也就是value,所以缺乏一个获取Key的方式)。

template<class T>
struct RBTreeNode
{
    T _data;
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    Colour _col;
    
    RBTreeNode(const T& data)
        :_data(data)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _col(RED)
    {}
};

template<class K, class T, class KeyOfT>
class RBTree
{
    typedef RBTreeNode<T> Node;
public:
    //各个成员函数
private:
    Node* _root = nullptr;
};

2.增加迭代器

新建一个迭代器类,用Ref和Ptr两个模板类型规定iterator和const_iterator

迭代器内部储存一个Node*指针,而且迭代器的++(加加)按照中序遍历的方式访问,--(减减)按中序遍历的反方向访问。

对于++重载,由于遍历方式是中序遍历,所以在迭代器锁定在某一个位置时,它一定遍历完了左树和根节点,它就需要开始遍历右树,而开始遍历右树需要先从右树的最左节点开始,所以先向右走再一直向左走到头即可。不过还有一种情况,如果右树为空,那么这个子树实际上已经遍历完了,那么我们就需要向上走,向上走一个节点后如果这个节点是上一个节点的右子树根,那么证明上一层的子树也已经遍历完成,那就继续向上进,直到这个节点是上一个节点的左子树根,此时就证明它上一个节点的左子树遍历完成,需要走到它的根上。

对于--重载也很相似,锁定某一位置时它左子树遍历完成,那么就需要走到左子树的最右节点。如果左树为空同样向上走直到这个节点是上一个节点的右子树,走到父节点上即可。

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
    typedef RBTreeNode<T> Node;
    typedef __RBTreeIterator<T, Ref, Ptr> Self;
    typedef __RBTreeIterator<T, T&, T*> iterator;
    
    Node* _node;
    
    __RBTreeIterator(Node* node)
        :_node(node)
    {}

    __RBTreeIterator(const iterator& s)
        :_node(s._node)
    {}
    
    Ref operator*()
    {
        return _node->_data;
    }

    Ptr operator->()
    {
        return &_node->_data;
    }
    
    Self& operator++()
    {
        if (_node->_right)
        {
            Node* min = _node->_right;
            while (min->_left)
            {
                min = min->_left;
            }
            _node = min;
        }
        else
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_right)
            {
                cur = cur->_parent;
                parent = parent->_parent;
            }
            _node = parent;
        }
        return *this;
    }

    Self& operator--()
    {
        if (_node->_left)
        {
            Node* max = _node->_left;
            while (max->_right)
            {
                max = max->_right;
            }
            _node = max;
        }
        else
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_left)
            {
                cur = cur->_parent;
                parent = parent->_parent;
            }
            _node = parent;
        }
        return *this;
    }

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

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

3.修改成员函数

一个是配套的迭代器和相关函数,另一个是成员函数对原来使用pair的部分进行修改,比如说KeyOfT仿函数定义对象来获取Key,以下为修改结果:

enum Colour
{
    RED,
    BLACK,
};

template<class T>
struct RBTreeNode
{
    T _data;
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    Colour _col;
    
    RBTreeNode(const T& data)
        :_data(data)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _col(RED)
    {}
};

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
    typedef RBTreeNode<T> Node;
    typedef __RBTreeIterator<T, Ref, Ptr> Self;
    typedef __RBTreeIterator<T, T&, T*> iterator;

    Node* _node;

    __RBTreeIterator(Node* node)
        :_node(node)
    {}

    __RBTreeIterator(const iterator& s)
        :_node(s._node)
    {}

    Ref operator*()
    {
        return _node->_data;
    }

    Ptr operator->()
    {
        return &_node->_data;
    }

    Self& operator++()
    {
        if (_node->_right)
        {
            Node* min = _node->_right;
            while (min->_left)
            {
                min = min->_left;
            }

            _node = min;
        }
        else
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_right)
            {
                cur = cur->_parent;
                parent = parent->_parent;
            }

            _node = parent;
        }

        return *this;
    }

    Self& operator--()
    {
        if (_node->_left)
        {
            Node* max = _node->_left;
            while (max->_right)
            {
                max = max->_right;
            }

            _node = max;
        }
        else
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_left)
            {
                cur = cur->_parent;
                parent = parent->_parent;
            }

            _node = parent;
        }

        return *this;
    }

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

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

};


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


    iterator begin()
    {
        Node* left = _root;
        while (left && left->_left)
        {
            left = left->_left;
        }

        return iterator(left);
    }

    iterator end()
    {
        return iterator(nullptr);
    }


    const_iterator begin() const
    {
        Node* left = _root;
        while (left && left->_left)
        {
            left = left->_left;
        }

        return const_iterator(left);
    }

    const_iterator end() const
    {
        return const_iterator(nullptr);
    }

    pair<iterator, bool> Insert(const T& data)
    {
        if (_root == nullptr)
        {
            _root = new Node(data);
            _root->_col = BLACK;
            return make_pair(iterator(_root), true);
        }

        KeyOfT kot;
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (kot(cur->_data) < kot(data))
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (kot(cur->_data) > kot(data))
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return make_pair(iterator(cur), false);
            }
        }

        cur = new Node(data);
        Node* newnode = cur;
        cur->_col = RED;
        if (kot(parent->_data) < kot(data))
        {
            parent->_right = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_left = cur;
            cur->_parent = parent;
        }

        while (parent && parent->_col == RED)
        {
            Node* grandfater = parent->_parent;
            if (parent == grandfater->_left)
            {
                Node* uncle = grandfater->_right;
                // 情况一  uncle存在且为红
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfater->_col = RED;

                    cur = grandfater;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_left)
                    {
                        // 情况二
                        RotateR(grandfater);
                        parent->_col = BLACK;
                        grandfater->_col = RED;
                    }
                    else
                    {
                        // 情况三
                        RotateL(parent);
                        RotateR(grandfater);
                        cur->_col = BLACK;
                        grandfater->_col = RED;
                    }

                    break;
                }
            }
            else // (parent == grandfater->_right)
            {
                Node* uncle = grandfater->_left;
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfater->_col = RED;

                    cur = grandfater;
                    parent = cur->_parent;
                }
                else
                {
                    //   g                
                    //      p
                    //         c
                    if (cur == parent->_right)
                    {
                        RotateL(grandfater);
                        parent->_col = BLACK;
                        grandfater->_col = RED;
                    }
                    else
                    {
                        //   g                
                        //      p
                        //   c
                        RotateR(parent);
                        RotateL(grandfater);
                        cur->_col = BLACK;
                        grandfater->_col = RED;
                    }

                    break;
                }
            }
        }

        _root->_col = BLACK;

        return make_pair(iterator(newnode), true);;
    }

    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;

        parent->_right = subRL;
        if (subRL)
            subRL->_parent = parent;

        Node* ppNode = parent->_parent;
        subR->_left = parent;
        parent->_parent = subR;


        if (ppNode == nullptr)
        {
            _root = subR;
            _root->_parent = nullptr;
        }
        else
        {
            if (ppNode->_left == parent)
            {
                ppNode->_left = subR;
            }
            else
            {
                ppNode->_right = subR;
            }

            subR->_parent = ppNode;
        }
    }

    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;

        parent->_left = subLR;
        if (subLR)
        {
            subLR->_parent = parent;
        }

        Node* ppNode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;

        if (ppNode == nullptr)
        {
            _root = subL;
            _root->_parent = nullptr;
        }
        else
        {
            if (ppNode->_left == parent)
            {
                ppNode->_left = subL;
            }
            else
            {
                ppNode->_right = subL;
            }

            subL->_parent = ppNode;
        }
    }

    void Inorder()
    {
        _Inorder(_root);
    }

    void _Inorder(Node* root)
    {
        if (root == nullptr)
            return;

        _Inorder(root->_left);
        cout << root->_kv.first << ":" << root->_kv.second << endl;
        _Inorder(root->_right);
    }

    bool Check(Node* root, int blackNum, const int ref)
    {
        if (root == nullptr)
        {
            if (blackNum != ref)
            {
                cout << "违反规则:本条路径的黑色节点的数量跟最左路径不相等" << endl;
                return false;
            }

            return true;
        }

        if (root->_col == RED && root->_parent->_col == RED)
        {
            cout << "违反规则:出现连续红色节点" << endl;
            return false;
        }

        if (root->_col == BLACK)
        {
            ++blackNum;
        }

        return Check(root->_left, blackNum, ref)
            && Check(root->_right, blackNum, ref);
    }

    bool IsBalance()
    {
        if (_root == nullptr)
        {
            return true;
        }

        if (_root->_col != BLACK)
        {
            return false;
        }

        int ref = 0;
        Node* left = _root;
        while (left)
        {
            if (left->_col == BLACK)
            {
                ++ref;
            }

            left = left->_left;
        }

        return Check(_root, 0, ref);
    }

    iterator find(const T& data)
    {
        return _find(data, _root);
    }
    
    iterator _find(const T& data, Node* root)
    {
        if (root == nullptr)
            return false;
        if (data < root->_data)
            _find(data, root->_left);
        else if (data < root->_data)
            _find(data, root->_right);
        else
            return iterator(root);
    }
private:
    Node* _root = nullptr;
};

template<class K>
struct SetKeyOfT
{
    const K& operator()(const K& key)
    {
        return key;
    }
};

4.建立map和set的头文件

set<K>在底层就是:RBTree<K, K, SetKeyOfT> _t,也就是Key和value都是key,SetKeyOfT直接返回value就是返回key

set.h

#pragma once

#include "RBTree.h"

namespace set
{
    template<class K>
    class set
    {
        struct SetKeyOfT
        {
            const K& operator()(const K& key)
            {
                return key;
            }
        };
    public:
        typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
        typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

        iterator begin() const
        {
            return _t.begin();
        }

        iterator end() const
        {
            return _t.end();
        }

        pair<iterator, bool> insert(const K& key)
        {
            pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
            return pair<iterator, bool>(ret.first, ret.second);
        }
    private:
        RBTree<K, K, SetKeyOfT> _t;
    };
}

map<K, T>在底层就是:RBTree<K, pair, MapKeyOfT> _t,K是key,而value是一个pair,MapKeyOfT直接返回value键值对。

map.h

#pragma once

#include "RBTree.h"

namespace map
{
    template<class K, class V>
    class map
    {
        struct MapKeyOfT
        {
            const K& operator()(const pair<const K, V>& kv)
            {
                return kv.first;
            }
        };
    public:
        typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
        typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;


        iterator begin()
        {
            return _t.begin();
        }

        iterator end()
        {
            return _t.end();
        }

        const_iterator begin() const
        {
            return _t.begin();
        }

        const_iterator end() const
        {
            return _t.end();
        }

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

        V& operator[](const K& key)
        {
            pair<iterator, bool> ret = insert(make_pair(key, V()));
            return ret.first->second;
        }
    private:
        RBTree<K, pair<const K, V>, MapKeyOfT> _t;
    };
}

5.测试代码

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



int main()
{
    multimap<string, string> dict;
    dict.insert(make_pair("left", "左边"));
    dict.insert(make_pair("left", "剩余"));
    dict.insert(make_pair("string", "字符串"));
    dict.insert(make_pair("left", "xxx"));

    for (const auto& kv : dict)
    {
        cout << kv.first << ":" << kv.second << endl;
    }

    string arr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

    multimap<string, int> countMap;
    for (auto& e : arr)
    {
        auto it = countMap.find(e);
        if (it == countMap.end())
        {
            countMap.insert(make_pair(e, 1));
        }
        else
        {
            it->second++;
        }
    }

    for (const auto& kv : countMap)
    {
        cout << kv.first << ":" << kv.second << endl;
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值