STL之map详解

STL的set和map都是基于红黑树实现的,和stack,queue都是基于deque一样,它们仅仅是调用了RBTree提供的接口函数,然后进行外层封装即可。

set简介

set是一种关联式容器,其特性如下:

  • set以RBTree作为底层容器
  • 所得元素的只有key没有value,value就是key
  • 不允许出现键值重复
  • 所有的元素都会被自动排序
  • 不能通过迭代器来改变set的值,因为set的值就是键

第五点需要做一下说明。如果set中允许修改键值的话,那么首先需要删除该键,然后调节平衡,在插入修改后的键值,再调节平衡,如此一来,严重破坏了set的结构,导致iterator失效,不知道应该指向之前的位置,还是指向改变后的位置。所以STL中将set的迭代器设置成const,不允许修改迭代器的值。

map简介

map和set一样是关联式容器,它们的底层容器都是红黑树,区别就在于map的值不作为键,键和值是分开的。它的特性如下:

  • map以RBTree作为底层容器
  • 所有元素都是键+值存在
  • 不允许键重复
  • 所有元素是通过键进行自动排序的自动按Key升序排序,所以不能对map用sort函数
  • map的键是不能修改的,但是其键对应的值是可以修改的

在map中,一个键对应一个值,其中键不允许重复,不允许修改,但是键对应的值是可以修改的,原因可以看上面set中的解释。

multimap和multiset就在map和set的基础上,使他们的键可以重复,除此之外基本等同。

根据key值快速查找记录,查找的复杂度基本是Log(N)

详情请移步:https://blog.csdn.net/terence1212/article/details/52487656

map的使用

map对象是模板类,需要关键字和存储对象两个模板参数,例如下列构造函数:

map<int, string> mapStudent;

数据的插入

//第一种:用insert函数插入pair数据  
#include <map>    
#include <string>   
#include <iostream>    
using namespace std;    
int main()    
{    
    map<int, string> mapStudent;   
    mapStudent.insert(pair<int, string>(1, "student_one"));    
    mapStudent.insert(pair<int, string>(2, "student_two"));   
    mapStudent.insert(pair<int, string>(3, "student_three"));   
    map<int, string>::iterator iter;   
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)   
       cout<<iter->first<<' '<<iter->second<<endl;  
  
}  
//第二种:用insert函数插入value_type数据   
#include <map>    
#include <string>    
#include <iostream>    
using namespace std;  
int main()    
{   
    map<int, string> mapStudent;    
    mapStudent.insert(map<int, string>::value_type (1, "student_one"));    
    mapStudent.insert(map<int, string>::value_type (2, "student_two"));    
    mapStudent.insert(map<int, string>::value_type (3, "student_three"));    
    map<int, string>::iterator iter;   
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)    
       cout<<iter->first<<' '<<iter->second<<endl;  
  
//第三种:用数组方式插入数据  
#include <map>    
#include <string>    
#include <iostream>    
using namespace std;    
int main()    
{    
    map<int, string> mapStudent;    
    mapStudent[1] = "student_one";    
    mapStudent[2] = "student_two";    
    mapStudent[3] = "student_three";    
    map<int, string>::iterator iter;    
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)    
        cout<<iter->first<<' '<<iter->second<<endl;  
  
}  
第一种和第二种在效果上是完成一样的,用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值。


数据的遍历

//前向遍历前面已有,这里主要介绍利用反向迭代器    
#include <map>    
#include <string>    
#include <iostream>    
using namespace std;    
int main()    
{    
    map<int, string> mapStudent;   
    mapStudent.insert(pair<int, string>(1, "student_one"));    
    mapStudent.insert(pair<int, string>(2, "student_two"));    
    mapStudent.insert(pair<int, string>(3, "student_three"));   
    map<int, string>::reverse_iterator iter;                             //反向迭代器
    for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++)   //同时rbegin()和rend()与前面也有区别 
        cout<<iter->first<<"  "<<iter->second<<endl;  
  
}  


数据的查找

第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1

第二种:用find函数来定位数据出现位置,传入的参数是key,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器。

在这里需要提到的是begin()和end()两个成员,分别代表map对象中第一个条目和最后一个条目,这两个数据的类型是iterator. 注意:而vector中的end()是最后一个元素的后一个,有所区别。

#include <map>    
#include <string>    
#include <iostream>    
using namespace std;    
int main()    
{    
    map<int, string> mapStudent;    
    mapStudent.insert(pair<int, string>(1, "student_one"));    
    mapStudent.insert(pair<int, string>(2, "student_two"));  
    mapStudent.insert(pair<int, string>(3, "student_three"));    
    map<int, string>::iterator iter;   
    iter = mapStudent.find(1);    
    if(iter != mapStudent.end())    
       cout<<"Find, the value is "<<iter->second<<endl;    
    else    
       cout<<"Do not Find"<<endl;  
      
    return 0;  
}  


数据的删除

移除某个map中某个条目用erase(),该成员方法的定义如下:

iterator erase(iterator it);                       //通过一个条目对象删除

iterator erase(iterator first,iterator last)//删除一个范围

size_type erase(const Key&key);              //通过关键字删除

clear()就相当于enumMap.erase(enumMap.begin(),enumMap.end());

#include <map>    
#include <string>    
#include <iostream>    
using namespace std;    
int main()    
{    
       map<int, string> mapStudent;    
       mapStudent.insert(pair<int, string>(1, "student_one"));    
       mapStudent.insert(pair<int, string>(2, "student_two"));    
       mapStudent.insert(pair<int, string>(3, "student_three"));  
  
        //如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好    
       //如果要删除1,用迭代器删除    
       map<int, string>::iterator iter;    
       iter = mapStudent.find(1);    
       mapStudent.erase(iter);  
  
       //如果要删除1,用关键字删除    
       int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0  
  
       //用迭代器,成片的删除    
       //一下代码把整个map清空    
       mapStudent.erase( mapStudent.begin(), mapStudent.end() );  
  
       //成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合    
       //自个加上遍历代码,打印输出吧  
  
}  

详情请移步:https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html

有关迭代器失效问题:

一般出现在循环erase

序列性容器::(vector和list和deque) 
        erase迭代器不仅使所指向被删元素的迭代器失效,而且使被删元素之后的所有迭代器失效,所以不能使用erase(iter++)的方式,但是erase的返回值为下一个有效的迭代器。

        所以正确方法为::
        for( iter = c.begin(); iter != c.end(); )
              iter = c.erase(iter);
 
关联性容器::(map和set比较常用)
        erase迭代器只是被删元素的迭代器失效,但是返回值为void,所以要采用erase(iter++)的方式删除迭代器,  
        所以正确方法为::
        for( iter = c.begin(); iter != c.end(); )  

              c.erase(iter++);

       原因如下:对于该语句cout << *p++ << endl; 很多人错误地以为是先执行*p, 然后执行p++, 其实, 这是个天大的误解。 大家可以查一下*和++的执行顺序, 虽然*和++的优先级相同, 但此处采取的是右结合方式, 实际上先执行的是p++, 不过, p++的返回值是原来的p。

      所以, 在m.erase(it++);中,it++先执行, 此时还没有erase, 程序自然不会崩溃. 当it++执行完后, 已经指向了下一个元素了, 但it++的返回值还是当前元素, 此时再删除它, 合情合理。

Tips:

        其实对于list两种方式都可以正常工作。

参考链接:https://blog.csdn.net/aa838260772/article/details/39678187

                 https://blog.csdn.net/stpeace/article/details/46507451


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值