• map和set的具体用法 【C++】


    关联式容器

    关联式容器里面存储的是结构的键值对,在数据检索时比序列式容器效率更高。比如:set、map、unordered_set、unordered_map等

    注意: C++STL当中的stack、queue和priority_queue属于容器适配器,它们默认使用的基础容器分别是deque、deque和vector

    键值对

    键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量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)
    	{
    	}
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    set

    1、set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列

    2、set当中存储元素的value都是唯一的,不可以重复,因此可以使用set进行去重

    3、与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放value,但在底层实际存放的是由构成的键值对,在set容器中插入元素时,只需要插入value即可,不需要构造键值对

    4、set中的元素不能被修改,set在底层是用二叉搜索树来实现的,若是对二叉搜索树当中某个结点的值进行了修改,那么这棵树将不再是二叉搜索树

    5、在内部,set中的元素总是按照其内部比较对象所指示的特定严格弱排序准则进行排序。当不传入内部比较对象时,set中的元素默认按照小于来比较

    6、set容器通过key访问单个元素的速度通常比unordered_set容器慢,但set容器允许根据顺序对元素进行直接迭代

    7、set在底层是用平衡搜索树(红黑树)实现的,所以在set当中查找某个元素的时间复杂度为logN

    set的定义方式

    //构造int类型的空容器
    set<int> s1; 
    
    //拷贝构造int类型s1
    set<int> s2(s1); 
    string str("abcdef");
    
    //拷贝string
    set<char> s3(str.begin(), str.end()); 
    
    //构造int类型的空容器,比较方式指定为大于
    set < int, greater<int>> s4; 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    set的使用

    void Testset()
    {
    	 //去重
    	set<int> s;
    	s.insert(1);
    	s.insert(4);
    	s.insert(3);
    	s.insert(3);
    	s.insert(2);
    	s.insert(2);
    	s.insert(3);
    	//遍历方式一
    
    	for (auto e : s)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    	//删除方式一
    	s.erase(3);
    	//遍历方式二
    
    	set<int>::iterator it = s.begin();
    	while (it != s.end())
    	{
    		cout << *it << " ";
    		it++;
    	}
    	cout << endl;
    	//删除方式二, 正向迭代器遍历 
    	set<int>::iterator pos = s.find(1);
    	if (pos!=s.end())
    	{
    		s.erase(pos);
    	}
    	//遍历方式三
    	set<int>::reverse_iterator rit = s.rbegin();
    	while (rit != s.rend())
    	{
    		cout << *rit << " ";
    		rit++;
    	}
    	cout << endl;
    	//容器中值为2的个数 
    	cout<<s.count(2);
    	cout << s.size();
    	s.clear();
    	cout << s.empty();
    
    }
    
    void TestmultiSet()
    {
    	//可以重复 
    	multiset<int> m;
    	m.insert(3);
    	m.insert(5);
    	m.insert(8);
    	m.insert(7);
    	m.insert(7);
    	m.insert(9);
    	m.insert(7);
    	for (auto e : m)
    	{
    		cout << e << "";
    	}
    	//find 
    	//set s;
    	//s.insert(1);
    	//s.insert(4);
    	//s.insert(3);
    	//s.insert(3);
    	//s.insert(2);
    	//s.insert(2);
    	//s.insert(3);
    	//if (s.find(3) != s.end())
    	//{
    	//	cout << "找到了" << " ";
    	//}
    	//else
    	//{
    	//	cout << "找不到" << " ";
    	//}
    	auto pos = m.find(7);//返回中序中第一个7
    	while (pos != m.end())
    	{
    		cout << *pos << " ";
    		pos++;
    	}
    	cout << endl;
    	cout << m.count(7) << endl;
    	auto ret = m.equal_range(17);
    	auto itlow = ret.first;
    	auto itup = ret.second;
    	//[itlow , itup) 左闭右开 左边界是第一个7,右边界是比7大的,才能完全删除所有的7
    	cout << *itlow<<endl;
    	cout << *itup<<endl;
    	m.erase(itlow, itup);//?
    	for (auto e : m)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    	
    }
    void Testset2()
    {
    	set<int> s;
    	s.insert(1);
    	s.insert(4);
    	s.insert(3);
    	s.insert(3);
    	s.insert(2);
    	s.insert(2);
    	s.insert(3);
    	//交换两个容器的数据 
    	set<int> tmp{ 11,22,33,44 };
    	s.swap(tmp);
    	for (auto e : s)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    }
    
    int main()
    {
    Testset();
    TestmultiSet();
    Testset2();
    return 0 ;
    }
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132

    multiset

    multiset容器与set容器的底层实现一样,都是平衡搜索树(红黑树),其次,multiset容器和set容器所提供的成员函数的接口都是基本一致的,multiset容器和set容器的唯一区别就是,multiset允许键值冗余,即multiset容器当中存储的元素是可以重复的。

    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	multiset<int> ms;
    	//插入元素(允许重复)
    	ms.insert(1);
    	ms.insert(4);
    	ms.insert(3);
    	ms.insert(3);
    	ms.insert(2);
    	ms.insert(2);
    	ms.insert(3);
    	for (auto e : ms)
    	{
    		cout << e << " ";
    	}
    	cout << endl; //1 2 2 3 3 3 4
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    map

    1、map是关联式容器,它按照特定的次序(按照key来比较)存储键值key和值value组成的元素,使用map的迭代器遍历map中的元素,可以得到有序序列。

    2、在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,并取别名为pair。

    3、map容器中元素的键值key不能被修改,但是元素的值value可以被修改,因为map底层的二叉搜索树是根据每个元素的键值key进行构建的,而不是值value。

    4、在内部,map中的元素总是按照键值key进行比较排序的。当不传入内部比较对象时,map中元素的键值key默认按照小于来比较。

    5、map容器通过键值key访问单个元素的速度通常比unordered_map容器慢,但map容器允许根据顺序对元素进行直接迭代。

    6、map容器支持下标访问符,即在[]中放入key,就可以找到与key对应的value。

    7、map在底层是用平衡搜索树(红黑树)实现的,所以在map当中查找某个元素的时间复杂度为logN

    map的定义方式

    map<int, double> m1; //构造一个key为int类型,value为double类型的空容器
    
    map<int, double> m2(m1); //拷贝构造key为int类型,value为double类型的m1容器的复制品
    
    map<int, double> m3(m2.begin(), m2.end()); //使用迭代器拷贝构造m2容器某段区间的复制品
    
    map<int, double, greater<int>> m4; //构造一个key为int类型,value为double类型的空容器,key比较方式指定为大于
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    insert

    在这里插入图片描述

    pair<iterator,bool> insert (const value_type& val);
    
    • 1

    value_type类型的,实际上value_type就是pair类型的别名:

    typedef pair<const Key, T> value_type;
    
    • 1

    插入元素时,需要用key和value构造一个pair对象,然后再将pair对象作为参数传入insert函数

    方式一:匿名对象

    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	map<int, string> m;
    	//方式一:调用pair的构造函数,构造一个匿名对象插入
    	m.insert(pair<int, string>(2, "two"));
    	m.insert(pair<int, string>(1, "one"));
    	m.insert(pair<int, string>(3, "three"));
    	for (auto e : m)
    	{
    		cout << "<" << e.first << "," << e.second << ">" << " ";
    	}
    	cout << endl; //<1,one> <2,two> <3,three>
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    方式二:调用make_pair函数模板插入(常用)

    库当中提供以下make_pair函数模板:

    template <class T1, class T2>
    pair<T1, T2> make_pair(T1 x, T2 y)
    {
    	return (pair<T1, T2>(x, y));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    向make_pair函数传入key和value,该函数模板会根据传入参数类型进行自动隐式推导,最终构造并返回一个对应的pair对象

    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	map<int, string> m;
    	//方式二:调用函数模板make_pair,构造对象插入
    	m.insert(make_pair(2, "two"));
    	m.insert(make_pair(1, "one"));
    	m.insert(make_pair(3, "three"));
    	for (auto e : m)
    	{
    		cout << "<" << e.first << "," << e.second << ">" << " ";
    	}
    	cout << endl; //<1,one> <2,two> <3,three>
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    insert函数的返回值
    在这里插入图片描述
    总结文档的内容:
    insert函数的返回值也是一个pair对象,该pair对象中第一个成员的类型是map的迭代器类型,第二个成员的类型的一个bool类型,具体含义如下:

    1、如果待插入元素的键值key在map当中不存在,则insert函数插入成功,并返回插入后元素的迭代器和true。
    2、如果待插入元素的键值key在map当中已经存在,则insert函数插入失败,并返回map当中键值为key的元素的迭代器和false。

    find

    在这里插入图片描述

    iterator find (const key_type& k);
    
    • 1

    根据所给key值在map当中进行查找,若找到了,则返回对应元素的迭代器,若未找到,则返回容器中最后一个元素下一个位置的正向迭代器。

    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	map<int, string> m;
    	m.insert(make_pair(2, "two"));
    	m.insert(make_pair(1, "one"));
    	m.insert(make_pair(3, "three"));
    	//获取key值为2的元素的迭代器
    	map<int, string>::iterator pos = m.find(2);
    	if (pos != m.end())
    	{
    		cout << pos->second << endl; //two
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    erase

    在这里插入图片描述
    根据key值删除指定元素,也可以根据迭代器删除指定元素,若是根据key值进行删除,则返回实际删除的元素个数。

    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	map<int, string> m;
    	m.insert(make_pair(2, "two"));
    	m.insert(make_pair(1, "one"));
    	m.insert(make_pair(3, "three"));
    	for (auto kv : m)
    	{
    		cout << kv.first<<" " << kv.second;
    	}
    	cout << endl;
    	//根据key值进行删除
    	m.erase(3);
    	for ( auto kv : m)
    	{
    		cout << kv.first << " " << kv.second;
    	}
    	//根据迭代器删除 
    	map<int, string>::iterator pos = m.find(2);
    	while (pos != m.end())
    	{
    		m.erase(pos);
    	}
    	for (auto kv : m)
    	{
    		cout << kv.first << " " << kv.second;
    	}
    	return 0;
    }
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    []运算符重载

    在这里插入图片描述
    在这里插入图片描述
    [ ]运算符重载函数的参数就是一个key值,而这个函数的返回值如下:

    (* (  (this->insert(  make_pair(k, mapped_type() ) ) ).first)   ).second
    
    • 1

    根据上述代码可以推断出[ ],运算符重载实现的逻辑:

    mapped_type& operator[] (const key_type& k)
    {
    	//1、调用insert函数插入键值对
    	pair<iterator, bool> ret = insert( make_pair(k, mapped_type() ) );
    	//2、拿出从insert函数获取到的迭代器
    	iterator it = ret.first;
    	//3、返回该迭代器位置元素的值value
    	return it->second;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    []运算符的使用

    int main()
    {
    		 map<string, string> dict;
    		 dict.insert(make_pair("string", "字符串"));
    		 dict.insert(make_pair("sort", "排序"));
    		 dict.insert(make_pair("insert", "插入"));
    	
    		 cout << dict["sort"] << endl;//查找和读
    		 dict["map"];//插入
    		 dict["map"] = "映射,地图";//修改
    		 dict["insert"] = "xxx";//修改
    		 dict["set"] = "集合";//插入+修改
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    总结一下:
    1、如果k不在map中,则先插入键值对,然后返回该键值对中V对象的引用。
    2、如果k已经在map中,则返回键值为k的元素对应的V对象的引用。

    map的迭代器遍历

    int main()
    {
    	map<int, string>  m;
    	m.insert(make_pair(2, "two"));
    	m.insert(make_pair(1, "one"));
    	m.insert(make_pair(3, "three"));
    
    	//正向迭代器 
    	map<int, string>::iterator   it = m.begin();
    	while (it != m.end())
    	{
    		//cout << (*it).first << ":"<<(*it).second<
    		cout << it->first << ":" << it->second << endl;//迭代器重载operator->
    		it++;	
    	}
    	cout << endl;
    	//反向迭代器 
    	map<int, string>::reverse_iterator rit = m.rbegin();
    	while (rit != m.rend())
    	{
    		cout << " " << rit->first << " " << rit->second;
    		rit++;
    	}
    	cout << endl;
    	//范围for ,kv就是*it
    	for (auto kv : m)
    	{
    		cout << kv.first <<" " << kv.second;
    	}
    	return 0;
    }
    
    • 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
    • 28
    • 29
    • 30
    • 31

    multimap

    multimap容器与map容器的底层实现一样,也都是平衡搜索树(红黑树),multimap容器和map容器的区别与multiset容器和set容器的区别一样,multimap允许键值冗余,即multimap容器当中存储的元素是可以重复的。

    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	multimap<int, string> mm;
    	//插入元素(允许重复)
    	mm.insert(make_pair(2, "two"));
    	mm.insert(make_pair(2, "double"));
    	mm.insert(make_pair(1, "one"));
    	mm.insert(make_pair(3, "three"));
    	for (auto e : mm)
    	{
    		cout << "<" << e.first << "," << e.second << ">" << " ";
    	}
    	cout << endl; //<1,one> <2,two> <2,double> <3,three>
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述
    如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注
    你们的每一次支持都将转化为我前进的动力!!

  • 相关阅读:
    牛客小白月赛80 A-D
    非零基础自学Golang 2 开发环境 2.2 配置GOPATH
    6.26CF模拟赛D:黑白条题题解
    【架构】负载均衡SLB浅谈
    FreeRTOS_事件标志组
    宜搭低代码开发高级认证例题1-待办列表
    C#的DataGridView数据控件(直接访问SQL vs 通过EF实体模型访问SQL)
    y148.第八章 Servless和Knative从入门到精通 -- Pub/Sub(十二)
    网络第一颗
    git commit 不进入 Vim 编辑模式,提示 ihint: Waiting for your editor to close the file...
  • 原文地址:https://blog.csdn.net/qq_73478334/article/details/133214906