C++ set和map的使用

关联式容器

set

介绍

 set的模板参数:

使用

构造函数

赋值重载

Modifiers

迭代器

Capacity

Operations

multiset

map

介绍

map的模板参数

​编辑

map的成员类型

使用

构造函数

赋值重载

Modifiers

迭代器

 Operations

[ ] 的重载(重点)

multimap


关联式容器


                vector、list、deque等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。

                关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的键值对,在数据检索时比序列式容器效率更高

键值对:

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

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)
    {}
};

树形结构的关联式容器:

        STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:set、map、multiset、multimap。这四种容器的共同点是:使用平衡二叉搜索树(红黑树)作为其底层,容器中的元素是一个有序的序列。

set


介绍

        使用平衡二叉搜索树(红黑树)作为其底层。

  • set是按照一定次序存储元素的容器
  • 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
  • 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
  • set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
  • set在底层是用二叉搜索树(红黑树)实现的。
  • set没有[]重载。

注意:

  • 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<key, key>构成的键值对。
  • set中插入元素时,只需要插入value即可,不需要构造键值对。
  • set中的元素不可以重复(因此可以使用set进行去重)。
  • 使用set的迭代器遍历set中的元素,可以得到有序序列
  • set中的元素默认按照小于来比较
  • set中查找某个元素,时间复杂度为:㏒₂N(实际上是二叉搜索树的高度次)
  • set中的元素不允许修改
  • set中的底层使用二叉搜索树(红黑树)来实现。

 set的模板参数:

class T 是 set 中存放元素的类型,实际在底层存储 <Key,Key> 的键值对

class Compare = less 是仿函数,使用缺省值即可,有需要再传参,set 中元素默认按照小于来比较

class Alloc = allocator 是空间配置器

使用

构造函数

set提供的构造函数有:第一个空构造和 第二个迭代器区间构造,一般都使用空构造,第三个是拷贝构造函数

void test_set1()
{
	set<int> s1;//空构造
	set<int> s2(s1.begin(), s1.end());//区间构造
	set<int> s3(s1);//拷贝构造
}

赋值重载

s3 = s1;//赋值重载

Modifiers

void test_set1()
{
	set<int> s;
	s.insert(1); // 插入元素
	s.insert(2);
	s.insert(3);
	s.insert(4);
	s.insert(7);
	s.insert(5);
	s.insert(6);

	s.erase(4);
	s.erase(40); // 没有该元素,删除也不会报错
}

迭代器

	// 排序加去重
	for (auto& i : s) // 范围for循环
	{
		//*it = 2; // 不能修改
		cout << i << " ";
	}
	cout << endl;

	set<int>::iterator it = s.begin(); // 迭代器
	while (it != s.end()) 
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

Capacity

max_size 返回可以容纳的元素最大数

Operations

find:

set<int>::iterator pos = s.find(3); // 没找到返回end() O(㏒₂N) set自己的
set<int>::iterator pos = find(s.begin(),s.end(), 3); // 没找到返回end() O(N)

 count:

	std::set<int> myset;

	// set some initial values:
	for (int i = 1; i < 5; ++i) myset.insert(i * 3);    // set: 3 6 9 12

	for (int i = 0; i < 10; ++i)
	{
		std::cout << i;
		if (myset.count(i) != 0)
			std::cout << " is an element of myset.\n";
		else
			std::cout << " is not an element of myset.\n";
	}

lower_bound(30) : 返回小于等于30的第一个迭代器

upper_bound(60):返回大于等于60的第一个迭代器

	std::set<int> myset;
	std::set<int>::iterator itlow, itup;

	for (int i = 1; i < 10; i++) myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90

	itlow = myset.lower_bound(30);                     //       ^
	itup = myset.upper_bound(60);                      //                   ^
	// 从itlow删除到itup
	myset.erase(itlow, itup);                     // 10 20 70 80 90

	std::cout << "myset contains:";
	for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)
		std::cout << ' ' << *it;
	std::cout << '\n';

 equal_range:

multiset


multiset的使用与set一致,最大区别:set去重+排序,multiset元素可重复+排序

multiset查找的元素是相同元素的时候,返回的是遍历中序遇到相同的第一个元素的迭代器

	// 跟set的区别允许键值冗余
	// 查找的是第一个出现的
	multiset<int> ms;
	ms.insert(1);
	ms.insert(1);
	ms.insert(1);
	ms.insert(1);
	ms.insert(1);
	ms.insert(1);
	for (auto& i : ms)
	{
		cout << i << " ";
	}cout << endl;

与set一样的头文件

#include<set>

map


介绍

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

map的模板参数

class Key 是键值对中 key 的类型

class T 是键值对中 value 的类型

class Compare = less 是仿函数,,使用缺省值即可,有需要再传参,默认按照小于来比较

class Alloc = allocator > 是空间配置器,现在不用理会

map的成员类型

key_type 是第一个模板参数(Key)

mapped_type 是第二个模板参数(T)

value_type 是 pair 进行 typedef 得到的

key_compare 是第三个模板参数(Compare),比较的是 key_type(Key),缺省值为 less

value_compare是用于比较元素的嵌套函数类

注意:

        pair(value_type )才是map存的值,mapped_type是pair::second的模板类型。

使用

构造函数

void Test_Map()
{
	//KV模型,通过 Key查找Value
	//map<Key, Value>
	map<int, int> m1;//空构造
 
	map<int, int> m2(m1.begin(), m1.end());//区间构造
	map<int, int> m3(m1);//拷贝构造
}

赋值重载

m3 = m1;

Modifiers

 insert:

void test_map1()
{
	map<int, int> m;
	//m.insert(1, 1); // 编译出错
	m.insert(pair<int, int>(1, 1));
	m.insert(pair<int, int>(3, 3));
	m.insert(pair<int, int>(2, 2)); // pair的构造函数,构造一个匿名对象
	m.insert(make_pair(4, 4)); // 函数模板构造一个pair对象
}

make_pair 的定义: 

erase:

m.erase(2);
m.erase(6);	// 没找到删除也不会报错

迭代器

	map<int, int>::iterator it = m.begin();
	while (it != m.end())
	{
		cout << (*it).first << ":" << (*it).second << endl;
		cout << it->first << ":" << it->second << endl;
		it++;
	}
	cout << endl;

	for (auto& i : m)
	{
		cout << i.first << ":" << i.second << endl;
	}
	cout << endl;

Capacity

 Operations

 find

map<string, int>::iterator ret = countMap.find("西瓜");// 没找到返回end() O(㏒₂N) 

[ ] 的重载(重点)

 set没有[ ] 的用法

首先做一道题 统计水果出现的次数

第一种方法

	string strArr[] = { "西瓜","西瓜","桃子","桃子","柚子","桃子","黄瓜","西瓜","西瓜" };

	map<string, int> countMap;
	// 1、
	for (auto& str : strArr)
	{
		map<string, int>::iterator ret = countMap.find(str); // 没找到就插入
		if (ret == countMap.end())
		{
			countMap.insert(make_pair(str, 1));
		}
		else
		{
			ret->second++;
		}
	}
	for (auto& pair : countMap)
	{
		cout << pair.first << ":" << pair.second<<endl;
	}

第二种方法:

我们得了解insert的返回值

 

第一个方法的返回值解释

单个元素版本 (1) 返回一个pair,其成员 pair::first 设置为指向新插入的元素或映射中具有等效键的元素的迭代器。如果插入了新元素,则对中的pair::second设置为 true;如果等效键已存在即插入失败,则设置为 false。

所以

	string strArr[] = { "西瓜","西瓜","桃子","桃子","柚子","桃子","黄瓜","西瓜","西瓜" };

	map<string, int> countMap;
    //2、
    for (auto& str : strArr)
	{
		// 1.如果水果没在map中:则插入成功
		//2.如果水果已经在map中,插入失败,通过返回值拿到水果所在的节点迭代器,++次数
		pair<map<string, int>::iterator, bool> ret = countMap.insert(make_pair(str,1));// insert的返回值
		if (ret.second == false)
		{
			ret.first->second++;
		}
	}
	for (auto& pair : countMap)
	{
		cout << pair.first << ":" << pair.second << endl;
	}

 第三种方法:

	string strArr[] = { "西瓜","西瓜","桃子","桃子","柚子","桃子","黄瓜","西瓜","西瓜" };

	map<string, int> countMap;
    //3、
    for (auto& str : strArr)
	{
		// 1、如果水果不在map中,则operator[]会插入pair<str,0(即是int() )>,返回映射对象(次数)的引用进行了++
		// 2、如果水果在map中,则operator[]返回水果对应的映射对象(次数)的引用,对它++。.
		countMap[str]++;
	}

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

更加简洁,为什么可以这样呢? 

map重载[ ] 的官方文档

看完文档发现使用 [ ]  相当于调用 :

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

countMap[ str ] 时,都会先调用 insert, 不管 str 有没有插进去,都会返回对应str的迭代器

还可以这样用

	countMap["香蕉"]; // 插入
	countMap["香蕉"] = 1; // 修改
	cout << countMap["香蕉"] << endl; //查找
	countMap["哈密瓜"] = 5; // 插入 + 修改

multimap


multimap的使用与map一致,最大区别:map去重+排序,multimap元素可重复+排序

multimap查找的元素是相同元素的时候,返回的是遍历中序遇到相同的第一个元素的迭代器

multimap是不支持[]重载的

但也可以实现统计水果出现次数与第一种方法一样

头文件与map一样

#include<map>

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值