C++STL之map和set的使用

Posted 小赵小赵福星高照~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++STL之map和set的使用相关的知识,希望对你有一定的参考价值。

map和set

文章目录

set

set的使用

set的插入

#include<iostream>
#include<set>
using namespace std;

void test_set1()

	set<int> s;
	s.insert(3);
	s.insert(1);
	s.insert(4);
	s.insert(3);
	s.insert(7);

int main()

	test_set1();
	return 0;

set的遍历

//遍历:排序+去重
set<int>::iterator it = s.begin();
while (it != s.end())

    cout << *it << " ";
    ++it;

cout << endl;
//范围for遍历
for (auto e : s)

    cout << e << " ";

cout << endl;
set<int> copy(s);//拷贝构造
for (auto e : s)

    cout << e << " ";

set的find接口

#include<set>
void test_set()

    set<int> s;
    s.insert(4);
    s.insert(2);
    s.insert(3);
    s.insert(6);
    s.insert(1);
    s.insert(8); 
    set<int>::iterator ret = s.find(4);//  O(logN)
    //这两个有效率的区别
    auto ret = find(s.begin(),s.end(),4);//这个是暴力查找,在一段迭代器区间里进行查找 O(N)

find我们可以用算法里面的find接口,也可以用set提供的find接口:

set<int>::iterator ret = s.find(4);//O(logN)
    //这两个有效率的区别
auto ret = find(s.begin(),s.end(),4);//这个是暴力查找,在一段迭代器区间里进行查找 O(N)

但是这两个有效率的区别:set的底层是二叉搜索树的性质,时间复杂度为O(logN),而算法当中的find是在一段迭代器区间暴力查找,在一段迭代器区间里进行查找,时间复杂度O(N)

set的erase接口

删除接口可以传迭代器撒谎才能胡,也可以传val,也可以传迭代器区间

  • ```cpp
    #include
    void test_set()

    set s;
    s.insert(4);
    s.insert(2);
    s.insert(3);
    s.insert(6);
    s.insert(1);
    s.insert(8);
    set::iterator it = s.begin();
    while(it!=s.end())

    cout<<*it<<" “;
    ++it;

    cout<<endl;
    set::iterator ret = s.find(4);// O(logN)
    if(ret != s.end())

    s.erase(ret);

    //也可以这样删除
    s.erase(6);
    s.erase(60);
    for(auto e:s)

    cout<< e <<” ";

    cout<<endl;

    int main()

    test_set();
    return 0;
    
    

map的介绍

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
    typedef pair value_type;
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行
    直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。
SGI-STL中关于键值对的定义:

template <class T1, class T2>
struct pair

    typedef T1 first_type;
    typedef T2 second_type;
    T1 first;
    T2 second;
    pair(): first(T1()), second(T2())
    
    pair(const T1& a, const T2& b): first(a), second(b)
    
;

map

map的使用

map的模板参数

key: 键值对中key的类型
T: 键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
注意:在使用map时,需要包含头文件。

map的插入

void test_map1

    map<int,int> m;
    m.insert(pair<int,int>(1,1));
    m.insert(pair<int,int>(2,2));
    m.insert(pair<int,int>(3,3));

我们在插入时需要这样插入,为什么呢?

我们可以看到insert函数有一个参数,类型是value_type,value_type被typedef成了pair,用键值对来存储数据,我们也可以这样:

m.insert(make_pair(4,4));//函数模板去构造匿名对象返回,这个不需要写模板参数,自动去推

make_pair的实现:

template<class T1,class T2>
pair<T1,T2> make_pair(T1 x,T2 y)

    return (pair<T1,T2>(x,y));

那么为什么要将key和value封装起来呢?我们看下面的代码进行解释:

void test_map1

    map<int,int> m;
    m.insert(pair<int,int>(1,1));
    m.insert(pair<int,int>(2,2));
    m.insert(pair<int,int>(3,3));
    
    map<int,int>::iterator it = m.begin();
    while(it != m.end())
    
        cout<<*it<<" ";//这里编译不过,这里如果这样的话需要返回两个值key和value,而C++不支持返回两个值,要返回两个值就需要构成一个结构来返回
        ++it;
    
    cout<<endl;

*cout<<it<<endl;这里编译不过,这里如果这样的话需要返回两个值key和value,而C++不支持返回两个值,要返回两个值就需要构成一个结构来返回

所以需要这样用:

void test_map1()

    map<int,int> m;
    m.insert(pair<int,int>(1,1));
    m.insert(pair<int,int>(2,2));
    m.insert(pair<int,int>(3,3));
    
    map<int,int>::iterator it = m.begin();
    while(it != m.end())
    
        //cout<<(*it).first << ":" << (*it).second << endl;
        //operator* 返回的是节点中值的引用
        cout<<it->first<<":"<<it->second<<endl;//这里为了可读性省略了一个箭头
        //operator-> 返回的是节点中值的指针,也就是pair<k,v>*
        ++it;
    
    cout<<endl;

也支持范围for遍历:

void test_map1()

    map<int,int> m;
    m.insert(pair<int,int>(1,1));
    m.insert(pair<int,int>(2,2));
    m.insert(pair<int,int>(3,3));
  	for(auto& e:m)
    
        cout<<e.first<<":"<<e.second<<endl;
    
    cout<<endl;

简单英文翻译字典

#include<map>
void test_map2()

    map<string,string> dict;
    dict.insert(pair<string,string>("sort","排序"));
    dict.insert(make_pair("left","左边"));
    dict.insert(make_pair("string","字符串"));
    dict.insert(make_pair("right","右边"));
    dict.insert(make_pair("bed","床"));
    map<string,string>::iterator it = dict.begin();
    while(it !=dict.end())
    
        //it.operator*(),不能返回两个值,这里返回的是类对象
        //cout<<(*it).first<<" "<<(*it).second<<endl;//报错
        //it.operator->(),返回类对象指针
        cout<<it->first<<" "<<it->second<<endl;
        ++it;
    
    

int main()

    test_map2();
    return 0;

统计字符串个数

void test_map3()

    string str[] = "sort","sort","tree","insert","sort","tree","sort","test";
    map<string,int> countMap;//统计字符串个数
    for(auto& e:str)
    
        auto ret = countMap.fin1d(e);//返回一个迭代器
        if(ret == countMap.end())
        
            //如果不存在,则插入
        	countMap.insert(make_pair(e,1));
            //countMap.insert(pair<>(e,1));
        
        else
        
            //如果存在,则次数++
            //(*ret).second++;
            ret->second++;
        
    
    
    for(auto& kv:countMap)
    
        cout<<kv.first<<" "<<kv.second<<endl;
    

可以看到次数已经成功统计出来

统计次数的方式二:

正常来说如果insert插入成功了返回true,已经存在此时插入失败了返回false,但是我们可以看到insert的返回值是一个pair:

返回值是pair,其成员pair::first被设为一个迭代器,指向新插入的元素否则指向map中具有相等key的元素,如果插入了新元素,则将该对中的第二个元素pair::second设置为true;否则如果已经存在相等的key,则将其设置为false。

void test_map4()

    string str[] =  "sort","sort","tree","insert","sort","tree","sort","test" ;
    map<string, int> countMap;
    for (const auto& e : str)
    
        //先插入,如果str在map中,insert会返回str所在的节点的迭代器,++次数即可
        pair<map<string, int>::iterator, bool> ret = countMap.insert(make_pair(e, 1));
        //auto ret = countMap.insert(make_pair(e, 1));
        if (ret.second == false)
        
            //说明插入失败了,之前已经插入过了
            ret.first->second++;//ret.first是指向相同key的那个元素的迭代器,ret是insert返回的pair
        
    
    map<string, int>::iterator it = countMap.begin();
    while (it != countMap.end())
    
        cout << it->first << " " << it->second << endl;
        ++it;
    

operator[]的使用

统计次数的方式三(operator[]):

operator[]的实现:

mapped_type& operator[](const key_type& k)

	return (*((this->insert(make_pair(k,mapped_type()))).first)).second;//插入时的匿名对象默认值为0

operator[]的实现进行简化,也可以这样实现:

mapped_type& operator[](const key_type& k)

    pair<iterator,bool> ret = insert(make_pair(k,mapped_type()));//插入时的匿名对象默认值为0
	return ret.first->second;

这里也分两种情况:

  1. k在map中,insert插入失败,因为k已经有了,insert返回的pair会带出k在map中存储节点的迭代器,通过这个迭代器,我们可以拿到k对应的value值,进行返回。
  2. k不在map中,insert进行插入,插入的值是pair<k,value()>,insert返回新插入值节点的迭代器,通过这个迭代器,我们可以拿到k对应的value值,进行返回。

总结map的operator[]特征:

  1. k不存在时,插入默认构造函数生成缺省值的value的pair<k,v()>
  2. k存在时,返回k对应的value值
void test_map5()

    string str[] =  "sort","sort","tree","insert","sort","tree","sort","test" ;
    map<string,int> countMap;
    //[]的用法
    for(const auto& e:str)
    
        countMap[e]++;//countMap[]返回的是value
    
    for(const auto& kv:countMap)
    
        cout<<kv.first<<" "<<kv.second<<endl;
    

1、如果k不在map中,先插入<k,V()>,返回新插入节点中V对象的引用

2、如果k已经在map中,返回k所在节点中对应V对象的引用

关于[]的一些扩展学习

void test_map6()

    map<string,string> dict;
    dict.insert(make_pair("sort","排序"));
    dict["left"] = "左边";//插入+修改
    dict["insert"];//插入
    dict["insert"] = "插入";//已经存在所以只是修改
    dict["left"] = "左边、剩余";//已经存在所以只是修改

第一个是插入+修改,因为刚开始没有则插入,[]返回的是value,所以赋值相当于将他修改了,第二个只是插入,第三个只是修改,因为insert已经存在了就没有再插入只是修改,第四个也已经存在,所以只是修改

注意:

key不能被修改,但是value是可以被修改的:

#include<map>
void test_map()

    map<string,string> dict;
    dict.insert(pair<string,string>("sort","排序"));
    dict.insert(make_pair("left","左边"));
    dict.insert(make_pair("string","字符串"));
    dict.insert(make_pair("right","右边"));
    dict.insert(make_pair("bed","床"));
    map<string,string>::iterator it = dict.begin();
    while(it 6-6:STL之map和set

6-6-2:STL之map和set——map的基本使用

6-6-1:STL之map和set——set的基本使用

进阶C++STL之map

带你深入理解STL之Set和Map

C++从青铜到王者第二十篇:STL之setmapmultisetmultimap的初识