树形结构关联式容器--set、map、multiset、multimap介绍及使用

本文介绍了STL中的关联式容器,包括set、map、multiset和multimap,它们以树形结构存储键值对,set强调唯一性,map支持有序访问,而multiset和multimap允许数据重复。讲解了它们的使用方法、特性及基本操作如插入、删除和查找。
摘要由CSDN通过智能技术生成

关联式容器

之前讲解的STL部分容器,如:vector、list、deque统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。
本文主要讲解关联式容器,关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

键值对

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

树形结构的关联式容器

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

set介绍

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

注意

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

set使用

需要引入头文件

#include<set>
  • 创建一个空的set
	set<int> s;
  • 利用迭代器空间创建一个有值的set(默认递增)
	//利用迭代器空间创建一个有值的set
	int arr[] = { 1,2,3,4,4 };
	//由于set里面不存放重复数据,因此只能存储4个元素:1,2,3,4
	//天然去重
	set<int> s2(arr, arr+sizeof(arr)/sizeof(arr[0]));
  • 利用迭代器空间创建一个有值的set(实现递减)
	//利用迭代器空间创建一个有值的set,递减顺序
	#include<functional>
	
	int arr[] = { 102,3,4,1 };
	set<int, greater<int>> s2(arr, arr+sizeof(arr)/sizeof(arr[0]));
  • 正向遍历(递增顺序):利用正向迭代器,set迭代器不支持修改操作
	//获取迭代器
	//set迭代器遍历,数据天然有序(递增):本质迭代器进行中序遍历
	set<int>::iterator it = s2.begin();
	while (it != s2.end())
	{
		cout << *it << " ";
		//set迭代器不支持修改,因为底层是二叉搜索树,不能随意修改。
		++it;
	}
	cout << endl;
  • 反向遍历(递减顺序):利用反向迭代器
	//反向遍历:递减
	set<int>::reverse_iterator rit = s2.rbegin();
	while (rit != s2.rend())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
  • insert:插入失败:已有数据迭代器,false
  • insert:插入成功:新数据迭代器,true
	//insert:插入失败:已有数据迭代器,false
	pair<set<int>::iterator, bool> ret = s2.insert(2);
	cout << ret.second << " " << *ret.first << endl;
	//insert:插入成功:新数据迭代器,true
	ret = s2.insert(100);
	cout << ret.second << " " << *ret.first << endl;
  • erase():可以传入值,和迭代器位置/区间。不能传入非法位置,比如end
	s2.erase(1);
	s2.erase(s2.begin());
  • find()接口:有返回值,如果查询的值存在,则返回该迭代器位置,如果不存在返回end位置
	auto it = s2.find(12);
	cout << (it != s2.end()) << endl;
	it = s2.find(3);
	cout << (it != s2.end()) << endl;
  • count():有返回值,返回值为查询值的个数,由于set里面的值不重复,因此返回值只有0或者1。
	cout << s2.count(3) << endl;
	cout << s2.count(12) << endl;

map介绍

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

map使用

  • 需要引入头文件
#include<map>
  • 构造一个空的map
	map<int, int> m;
  • 构造带值的map:通过迭代器构建,迭代器需要pair类型(包含key和value)
	//创建了一个pair类型数组,里面有四个pair类型对象
	pair<int, int> arr[] = { pair<int, int>(5, 5), pair<int, int>(1, 2),pair<int, int>(3,3), pair<int, int>(0,0) };
	//map中key不能重复,value可以重复
	map<int, int> m2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	printMap(m2);
	printMap_r(m2);

其中map迭代器正向遍历(递增)输出实现如下:
不能直接解引用,而是通过指针指向输出值

template <class T1, class T2>
void printMap(const map<T1, T2>& m)
{
	//map中数据是pair
	//迭代器访问的顺序:按照key的中序遍历的顺序
	typename map<T1, T2>::const_iterator it = m.begin();
	while (it != m.end())
	{
		//不能直接输出pair对象
		cout << it->first << "-->" << it->second << endl;
		++it;
	}
}

其中map迭代器反向遍历(递减)输出实现如下:

template <class T1, class T2>
void printMap_r(const map<T1, T2>& m)
{
	//map中数据是pair
	//迭代器访问的顺序:按照key的中序遍历的顺序
	typename map<T1, T2>::const_reverse_iterator it = m.rbegin();
	while (it != m.rend())
	{
		//不能直接输出pair对象
		cout << it->first << "-->" << it->second << endl;
		++it;
	}
}

通过传入greater参数实现递减排序

	//通过传入greater参数实现递减排序
	map<int, int, greater<int>> m3(arr, arr + sizeof(arr) / sizeof(arr[0]));
	for (auto& p : m3)
	{
		//范围for拿出来的p是已经进行过解引用了,因此使用.
		cout << p.first << "-->" << p.second << endl;
	}
  • [ ]:传入key值可以对key值对应的value进行读取以及修改
    如果key值不存在,则创建key值value为缺省值
    如果key值存在,则返回该key值对应的value值
	//对key值为1的value进行输出
	cout << m2[1] << endl;
	对key值为1的value进行修改
	m2[1] = 100;
	cout << m2[1] << endl;

operator[]:内部过程

  1. 创建一个pair对象,内容:k—value缺省值
  2. 插入第一步创建的pair对象
    a.插入成功:返回pair《pair对象的迭代器,true>
    b.插入失败:返回pair<已经存在的键值为k的pair对象迭代器,false>
  3. 获取插入返回值的first成员:是一个map中pair对象的迭代器
  4. 解引用第三步中的迭代器:解引用之后为map中的pair对象
  5. 获取第四步中的pair对象的second成员:其实就是k对应的v:
    a.如果插入成功:v是缺省值
    b.如果插入失败:v是之前已存在的值
  • at接口和operator[]区别:at执行插入的操作,如果key不存在,抛异常
	//at接口和operator[]区别:at执行插入的操作,如果key不存在,抛异常
	cout << m2.at(1000) << endl;
  • 两种常见的插入方式
  1. 调用pair构造函数创建对象
  2. 调用make_pair函数创建对象
	//两种常见的插入方式:
	map<int, int> m3;
	//1.调用pair构造函数创建对象
	auto ret = m.insert(pair<int, int>(1, 1));
	//ret是一个pair类型对象,该pair类型里面包含一个迭代器和bool值(插入成功或者失败)
	// 第一个.first是调出迭代器,第二个->first是调出key值
	cout << ret.first->first << "-->" << ret.first->second << ret.second << endl;
	//2.调用make_pair函数创建对象
	ret = m.insert(make_pair(2, 2));
  • erase():删除,返回值为返回删除个数,由于key值不能重复,因此返回只有0或1
	size_t num = m.erase(2);
  • find():查找操作
	//查找按照key值查找
	auto it = m.find(1);
	cout << it->first << "-->" << it->second << endl;
	//查找失败,返回end迭代器
	it = m.find(222);
	cout << (it == m.end()) << endl;

multiset使用

  • multiset:可以存放重复的数据。set去重
	int arr[] = { 1,10,2,5,3,33,12,44,32,2,2,1 };
	//multiset:可以存放重复的数据
	multiset<int> s(arr, arr + sizeof(arr) / sizeof(arr[0]));
	for (auto& e : s)
	{
		cout << e << " ";
	}
	cout << endl;

其他接口类似。

multimap使用

  • multimap:key和value都可以重复
	multimap<int, int> m;
	m.insert(make_pair(10, 10));
	m.insert(make_pair(10, 11));
	m.insert(make_pair(10, 122));
	m.insert(make_pair(11, 10));
	m.insert(make_pair(10, 10));
	for (auto& p : m)
	{
		cout << p.first << "-->" << p.second << endl;
	}

注意:multimap没有[ ]的操作,因为key值可以重复,没有唯一性,不知道给哪个key赋值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值