map和set

来源:互联网 发布:c语言神奇海螺 编辑:程序博客网 时间:2024/05/20 08:43

说起map和set,想必我们都学过红黑树了吧,map和set就是红黑树的一个应用领域。它的底层就是由红黑树来实现的。下面简单说一下map和set的使用吧。

首先,有一个栗子是这样的,让我们统计出每种水果出现的次数。

我们会想到怎么解决的。关于map,我们知道,当你插入同样的key值时,它就不会将要插入的key值插入到map中。但是,我们还知道,map是有俩个参数的,一个是插入的key值,另一个value可以用来统计key值出现的次数。

关于统计水果次数的问题,我们主要有以下几种方法:

    map<string,int> countMap;    string strs[] = {"苹果","香蕉","橘子","苹果","苹果","香蕉"};    //1.从头到尾遍历看是否有数组中的值,没有则插入,有则使second++    for(int i = 0; i < sizeof(strs)/sizeof(strs[0]); i++)    {        map<string,int>::iterator it = countMap.find(strs[i]);        if(it != countMap.end())        {            it->second++;        }        else        {            countMap.insert(make_pair(strs[i],1));        }    }    map<string,int>::iterator it1 = countMap.begin();    while(it1 != countMap.end())    {        cout<<it1->first<<":"<<it1->second<<endl;        ++it1;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
//2.检测插入的返回值pair<map<string,int>::iterator,bool>的second是否为false来确定    pair<map<string,int>::iterator,bool> ret;    for(int i = 0; i < sizeof(strs)/sizeof(strs[0]);i++)    {        ret = countMap.insert(make_pair(strs[i],1));        if(ret.second == false)            ret.first->second++;    }    map<string,int>::iterator it1 = countMap.begin();    while(it1 != countMap.end())    {        cout<<it1->first<<":"<<it1->second<<endl;        ++it1;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
//3.直接使用map重载的[]    for(int i = 0; i < sizeof(strs)/sizeof(strs[0]);i++)    {        countMap[strs[i]]++;    }    map<string,int>::iterator it1 = countMap.begin();    while(it1 != countMap.end())    {        cout<<it1->first<<":"<<it1->second<<endl;        ++it1;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果:

这里写图片描述

这里需要注意的几点是:

1.首先使用插入的时候,是需要插入一个pair结构体,因为map底层的value是一个pair结构体,里边成员又有first和second;因此需要使用make_pair来构造insert的参数。
2.在使用第三种方法的时候,我们使用到map重载的operator[],说一下operator[]的函数:
(*((this->insert(make_pair(x,T()))).first)).second
分解一下上边的operator[]的式子:
this->insert(make_pair(x,T())):返回值为pair<iterator,bool>结构体
((this->insert(make_pair(x,T()))).first):表示pair结构体中的first,即指向一个pair结构体的迭代器,此pair结构体中有key和value,也即所谓的first和second
(*((this->insert(make_pair(x,T()))).first)).second:表示取迭代器中指向的pair的second

说到map,我们还有一个multimap,是用来插入冗余的值,比如有相同的key值的时候,对于map而言,它就不会将其插入,而对于multimap而言就会插入。典型的例子为字典,我们英译汉的时候,同一个英语单词代表着不同的意思,这时multimap就会将每一个key值对应的不同的value值都会插入,并且以排好序的方式显示。

1.比如map来显示字典的时候:

typedef map<string,string> Dict;    typedef map<string,string>::iterator DictIt;    Dict dict;    dict.insert(make_pair("left","左边"));    dict.insert(make_pair("right","右边"));    dict.insert(make_pair("left","剩余"));    DictIt it = dict.begin();    while (it != dict.end())    {        cout<<it->first<<":"<<it->second<<endl;        ++it;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

结果为:

这里写图片描述

2.用multimap来实现字典的时候:

typedef multimap<string,string> Dict;    typedef multimap<string,string>::iterator DictIt;    Dict dict;    dict.insert(make_pair("left","左边"));    dict.insert(make_pair("right","右边"));    dict.insert(make_pair("left","剩余"));    DictIt it = dict.begin();    while (it != dict.end())    {        cout<<it->first<<":"<<it->second<<endl;        ++it;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果:

这里写图片描述

综上所述,
(1)map和multimap可以通过key来找value,也可通过key排序
(2)当我们查找某个key值的时候,发现有多个相同的key值,此时不知道it该指向哪个pair结构体,这里要说明的是,它将返回中序遍历的第一个key值

验证一下:

typedef multimap<string,string> Dict;    typedef multimap<string,string>::iterator DictIt;    Dict dict;    dict.insert(make_pair("left","左边"));    dict.insert(make_pair("right","右边"));    dict.insert(make_pair("left","剩余"));    DictIt it = dict.begin();    it = dict.find("left");    dict.erase(it);    it = dict.begin();    while (it != dict.end())    {        cout<<it->first<<":"<<it->second<<endl;        ++it;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里,我们找到一个key值为left的pair,此时删除它后再打印一下发现得出的是比它大的value值。

这里写图片描述

说完map和multimap后,与之对应的还有set和multiset。set和multiset是用来判断这个值存在或者不存在。其次也可以用来排序。还有一个特点是过滤(去重)。

1.检测其存在或者不存在

void TestSet(){    typedef set<string> MySet;    typedef set<string>::iterator MySetIt;    MySet myset;    string strs[] = {"苹果","香蕉","橘子","西瓜","草莓","樱桃"};    for(int i = 0; i < sizeof(strs)/sizeof(strs[0]); i++)    {        myset.insert(strs[i]);    }    MySetIt it = myset.begin();    if(it == myset.find("哈密瓜"))            cout<<"哈密瓜存在"<<endl;        else            cout<<"哈密瓜不存在"<<endl;    cout<<"存在的其他水果为:"<<endl;    it = myset.begin();    while(it != myset.end())    {        cout<<*it<<endl;        ++it;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

运行结果:

这里写图片描述

2.排序和去重

typedef set<int> MySet;    typedef set<int>::iterator MySetIt;    MySet myset;    for(int i = 10; i > 0; i--)        myset.insert(i);    myset.insert(5);    MySetIt it = myset.begin();    while(it != myset.end())    {        cout<<*it<<endl;        ++it;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

运行结果:

这里写图片描述

对于过滤来说,就是如果有相同的key值,它就会去掉相同的key,只插入一个到set中。

这里multiset的意义和multimap差不多,也是处理冗余的数据。使用方法类似。

对于map和set的底层是怎么实现的呢。它是通过写的一个红黑树。主要的区别是

里边的value_type的意义,对于map来说,value_type指的是一个pair的结构体,结构体成员为key和value,而对于set来说,value_type指的是key值。
在红黑树中,用了枚举来表示颜色。而在源码的红黑树中使用了bool值来代替红黑俩种颜色
我们还知道,map和multimap,set和multiset也有区别,底层是怎么用红黑树的呢。它是插入的时候分别对红黑树的插入分为唯一插入(insert_unique)和相等插入(insert_equal)(相等插入就是对冗余数据的考虑)。
set的插入:

map中value_type: typedef pair<const Key, T> value_type;
set中value_type: typedef Key value_type;

set的插入:

  typedef  pair<iterator, bool> pair_iterator_bool;   pair<iterator,bool> insert(const value_type& x)   {     pair<typename rep_type::iterator, bool> p = t.insert_unique(x);     return pair<iterator, bool>(p.first, p.second);  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

map的插入:

pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); }
  • 1
  • 2
  • 1
  • 2

multimap的插入:

  iterator insert(const value_type& x)   { return t.insert_equal(x); }
  • 1
  • 2
  • 1
  • 2

multiset的插入:

  iterator insert(const value_type& x)   {     return t.insert_equal(x);  }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

还可以进行相关的区间的插入,删除,某个位置的插入删除等操作。

小姿势:

lower_bound : 用来找到比key值大的数upper_bound : 用来找到比key值大的数
原创粉丝点击