C++ STL关联容器

关联容器

关联容器支持高效的关键字查找和访问。
两个主要的关联容器(associative container)类型是 map 和 set。
map 中的元素是一些{关键字,值}对。
关键字起到索引的作用,值则表示与索引相关联的数据。
set 中的每个元素只包含一个关键字。
set 支持高效的关键字查询操作——检查一个给定关键字是否在 set 中。
标准库提供 8 个关联容器:

容器功能
map关联数组:保存{关键字,值}
set关键字即值,即只保存关键字的容器
multimap关键字可重复出现的 map
multiset关键字可重复出现的 set
unordered_map用哈希函数组织的 map
unordered_set用哈希函数组织的 set
unordered_multimap哈希函数组织的map,关键字可重复出现
unordered_multiset哈希函数组织的set,关键字可重复出现

允许重复关键字的容器的名字中包含 mutli;不保持关键字按顺序存储的容器的名字都以单词 unordered 开头。

关联容器特有的成员

类型别名功能
key_type关键字类型,仅关联容器具有
mapped_type映射值类型,仅关联容器具有
key_compare比较标准类型,仅关联容器具有
hasher哈希函数类型,仅无序容器具有
key_equal等价性检验函数类型,仅无序容器具有
local_iterator桶迭代器类型,仅无序容器具有
const_local_iterator桶迭代器类型,仅无序容器具有

定义和初始化关联容器

默认初始化

	map <int, string> M1;
	set <char> Set; 

列表初始化

当初始化一个 map 时,必须提供关键字类型和值类型;每一个关键字—值对包围在花括号中{ key , value }来指出它们一起构成了 map 中的一个元素。

	map <int, string> M1 {
		{1,"C++"},{2,"Java"},{5,"Python"}
	};

当初始化一个 set 时,只需提供关键字类型,关键字类型就是元素类型。

	set <char> Set1 { 'A','B','C' };

一个 map 或 set 中的关键字必须是唯一的;容器 mulitmap 和 mulitset 没有这个限制,它们都允许多个元素具有相同的关键字。

	map <int, string> M1{
		{100,"C++"},{2,"Java"},{100,"Python"},{100,"C"}
	};
	cout <<M1.size() << endl;  // 内含 2 个元素
	multimap <int, string> M2{
		{100,"C++"},{2,"Java"},{100,"Python"},{100,"C"}
	};
	cout << M2.size() << endl;	// 内含 4 个元素
	
	set <char> Set1{ 'A','B','C','B','B' };
	cout << Set1.size() << endl; // 内含 3 个元素
	multiset <char> Set2{ 'A','B','C','B','B' };
	cout << Set2.size() << endl; // 内含 5 个元素

将一个新容器创建为另一个容器的拷贝

拷贝整个容器

	map <int, string> M1 {
		{1,"C++"},{2,"Java"},{5,"Python"}
	};
	map <int, string> M2{ M1 };
	set <char> Set1 { 'A','B','C' };
	set <char> Set2 { Set1 };

拷贝迭代器指定范围中的元素

	map <int, string> M1 {
		{1,"C++"},{2,"Java"},{5,"Python"}
	};
	map <int, string> M2{ M1.begin(),M2.end() };
	set <char> Set1 { 'A','B','C' };
	set <char> Set3{ Set1.begin(),Set1.end() };

除了容器通用的构造函数外,关联容器还提供了允许程序员指定比较器的构造函数:

成员函数功能
map m {cmp,a};用比较器 cmp 和分配器 a 构造 m;显示构造函数
map m {cmp};map m {cmp,A{}}; ;显示构造函数
map m {};map m {C{}}; ;显示构造函数
map m {b,e,cmp,a};用比较器 cmp 和分配器 a 构造 m;用 [b:e) 间的元素初始化
map m {b,e,cmp};map m {b,e,cmp,A{}};
map m {b,e};map m {b,e,C{}};
map m {m2};拷贝和移动构造函数
map m {a};默认构造 map;使用分配器 a;显示构造函数
map m {m2,a};从 m2 拷贝和移动构造 m;使用分配器 a
map m {{elem},cmp,a};用比较器 cmp 和分配器 a 构造 m;用 initializer_list{elem} 初始化元素
map m {{elem},cmp};map m {{elem},cmp,A{}};
map m {{elem}};map m {{elem},cmp,C{}};

关联容器迭代器

当解引用一个关联容器迭代器时,会得到一个类型为容器的 value_type 值的引用。
对于 map 而言,value_type 是一个 pair 类型,其 first 成员保存 const 的关键字,second 成员保存值。

	map <int, string> M1{
		{1,"C++"},{2,"Java"},{100,"Python"},{100,"C"}
	};
	auto map_it = M1.begin();       //value_type 值的引用
	cout<< map_it->first <<endl;    //输出 1,关键字值不允许修改
	cout << map_it->second << endl; //输出 C++

对于 set 而言,value_type 与 key_type 相同。

	set <char> Set1 {'A', 'B', 'C'};
	auto set_it = Set1.begin();
	cout << *set_it << endl;   //输出 A,关键字值不允许修改
遍历关联容器

当使用迭代器遍历有序容器时,迭代器按关键字升序遍历元素。
map

	map <int, string> M1{
		{5,"C++"},{2,"Java"},{100,"Python"},{10,"C"}
	};
	auto map_it = M1.cbegin();
	while ( map_it != M1.cend() )
	{
		cout <<'{'<< map_it->first <<
		',' << map_it->second << '}'<<' ';
		map_it++;
	}
	/*输出:{2,"Java"} {5,"C++"} {10,"C"} {100,"Python"}*/

set

	set <char> Set1 {'D', 'B', 'A','C'};
	auto set_it = Set1.cbegin();
	while ( set_it != Set1.cend() )
	{
		cout << *set_it++ << ' ';
	}
	/*输出:A B C D*/

关联容器的插入和查找

map 的下标操作

map 和 unordered_map 容器提供了下标运算符和一个对应的 at() 函数。
不能对一个 mutlimap 或 unordered_mutlimap 进行下标操作,因为该容器中可能有多个值与一个关键字相关联。
set 容器不支持下标访问,因为 set 中没有与关键字相关的值。
map 下标运算符接收一个索引(即,关键字)获取与此索引相关联的值。

	map <char, string> M1{
		{'A',"C++"},{'C',"Java"},{'D',"Python"},{'F',"C"}
	};
	cout << M1['A'] << endl;    //输出 C++
	cout << M1.at('C') << endl; //输出 Java

如果索引(关键字)并不在 map 中,下标运算符会为它创建一个元素并插入到 map 中,关联值将进行值初始化。

	map <int, char> M1{
		{1,'A'},{3,'B'},{5,'F'}
	};
	cout << (int)M1[2] << endl; //输出 0

如果索引(关键字)并不在 map 中,at() 函数会抛出一个 out_of_range 异常。
当对一个 map 进行下标操作时,会获得一个 mapped_type 对象。
下标运算符和 at() 函数只适用于非 const 的 map 和 unordered_mutlimap。

关联容器查找

对 map 使用 find() 代替下标操作

成员函数 find() 返回一个迭代器,指向第一个关键字为 k 的元素,若 k 不在容器中,则返回尾后迭代器

	map <char, string> M1{
		{'A',"C++"},{'C',"Java"},{'D',"Python"},{'F',"C"}
	};
	auto map_it = M1.find('C');
	cout << map_it->second << endl; //输出 Java
lower_bound() 和 upper_bound()

成员函数 lower_bound() 和 upper_bound() 不适用于无序容器。
成员函数 lower_bound() 返回一个迭代器,指向第一个关键字不小于 k 的元素。

	map <char, string> M1{
		{'A',"C++"},{'C',"Java"},{'D',"Python"},{'F',"C"}
	};
	auto map_it = M1.lower_bound('C');
	cout << map_it->second << endl; //输出 Java

成员函数 upper_bound() 返回一个迭代器,指向第一个关键字大于 k 的元素。

	map <char, string> M1{
		{'A',"C++"},{'C',"Java"},{'D',"Python"},{'F',"C"}
	};
	auto map_it = M1.upper_bound('C');
	cout << map_it->second << endl; //输出Python
在 multimap 或 multiset 中查找元素

方法一:
成员函数 count() 返回关键字等于 k 的元素的数量;对于不允许重复关键字的容器,返回值永远是 0 或 1.

	multimap <char, string> M1{
		{'A',"C++"},{'B',"Java"},{'B',"Python"},{'B',"C"}
	};
	/*输出所有与关键字'B'相关联的值 */
	auto map_it = M1.find('B');
	for (int i = 0; i < M1.count('B'); ++i)
	{
		cout << map_it->second  << ' ';
		map_it++;
	}

方法二:

	multimap <char, string> M1{
		{'A',"C++"},{'B',"Java"},{'B',"Python"},{'B',"C"}
	};
	/*输出所有与关键字'B'相关联的值 */
	auto map_it = M1.lower_bound('B');
	/*如果 lower_bound 和 upper_bound 返回相同的迭代器,则给定关键字不在容器中*/
	while ( map_it != M1.upper_bound('B') )
	{
		cout << map_it->second << endl;
		map_it++;
	}

方法三:
成员函数 equal_range() 返回一个迭代器 pair,表示关键字等于 k 的元素的范围;若 k 不存在,pair 的两个成员均等于尾后迭代器。

	multimap <char, string> M1{
		{'A',"C++"},{'B',"Java"},{'B',"Python"},{'B',"C"}
	};
	/*输出所有与关键字'B'相关联的值 */
	auto p = M1.equal_range('B');
	multimap <char, string>::iterator map_it = p.first;
	while ( map_it != p.second )
	{
		cout << map_it->second << endl;
		map_it++;
	}

向关联容器插入元素

向关联容器中插入一个元素。
map 的元素类型是 pair 类型。

	map <char, string> M1;
	M1.insert( {'A',"C++"} );
	M1.insert( make_pair('B',"Java") );
	M1.insert( pair <char, string> {'C', "Python"} );
	M1.insert( map <char, string>::value_type{ 'D',"C" } );
	set <char> Set;
	Set.insert('B');
	Set.insert('A');

成员函数 insert() 的返回值:

  • 对于不包含重复关键字的容器,添加单一元素的 insert() 的版本返回一个 pair 对象。
    • pair 的 first 成员是一个迭代器,指向具有给定关键字的元素;
    • pair 的 second 成员是一个 bool 值:
      • 如果关键字已存在容器中,则 insert 什么都不做,bool 值为 false。
      • 如果关键字不在容器中,则元素被插入到容器中,bool 值为 true。
	map <char, string> M1;
	M1.insert( {'A',"C++"} );
	M1.insert( make_pair('B',"Java") );

	auto p = M1.insert( { 'A',"C++" } );
	cout << p.first->second << endl; // 输出 C++
	cout << p.second << endl;        // 输出 0

向关联容器中插入一个花括号包围的元素值列表。

	vector <int> V1{ 1,3,5,7,9 };
	set <int> Set;
	Set.insert({ 2,4,6,8,10 });

向关联容器中插入一对迭代器指定范围的所有元素。
第一个迭代器指向要插入的第一个元素,第二个迭代器指向要插入的最后一个元素之后的位置。

	vector <int> V1{ 1,3,5,7,9 };
	set <int> Set;
	Set.insert( V1.begin(), V1.end() );

从关联容器中删除元素

成员函数 erase() 从容器中删除每个与关键字 k 相关联的元素;返回一个 size_type 值,指出删除的元素的数量。

	map <char, string> M1{
	{'A',"C++"},{'C',"Java"},{'D',"Python"},{'F',"C"}
	};
	M1.erase('A'); //删除 {'A',"C++"}
	set <char> Set{ 'B','C','D','E','A' };
	Set.erase('B'); //删除 B

成员函数 erase() 从容器中删除迭代器 p 指向的元素;p 必须指向容器中一个真实元素;返回一个指向 p 之后元素的迭代器。

	map <char, string> M1{
	{'A',"C++"},{'C',"Java"},{'D',"Python"},{'F',"C"}
	};
	M1.erase( M1.begin(),M1.end() ); //删除所有元素
	set <char> Set{ 'B','C','D','E','A' };
	auto set_its = Set.end();
	--set_its;
	Set.erase( Set.begin(),set_its ); //删除 A B C D
	

成员函数 erase() 删除一对迭代器指定范围内的所有元素;返回指向被删除的元素之后位置的迭代器。

	map <char, string> M1{
	{'A',"C++"},{'C',"Java"},{'D',"Python"},{'F',"C"}
	};
	auto map_its = M1.begin();
	++map_its;
	M1.erase(map_its); //删除 {'C',"Java"}
	set <char> Set{ 'B','C','D','E','A' };
	auto set_its = Set.end();
	--set_its;
	Set.erase( set_its ); //删除 E
删除容器内所有的元素

成员函数 clear() 删除容器内所有元素。

	set <char> Set{ 'B','C','D','E','A' };
	Set.clear();
成员函数功能
v = c[k];v 是指向关键字为 k 的元素的引用;若未找到,则插入(k,mapped_type());只适用于 map 和 unordered_map
v = c.at(k);v 是指向关键字为 k 的元素的引用;若未找到,则抛出一个 out_of_range;只适用于 map 和 unordered_map
p = c.find(k);p 是指向关键字为 k 的第一个元素或 c.end()(未找到)
p = c.lower_bound(k);p 是指向关键字大于等于 k 的第一个元素或 c.end()(未找到);只适用于有序容器
p = c.upper_bound(k);p 是指向关键字大于 k 的第一个元素或 c.end()(未找到);只适用于有序容器
pair(p1,p2) = c.equal_range(k);p1 = c.lower_bound(k);p2 = c.upper_bound(k)
pair(p,b) = c.insert(x);x 是一个 value_type 或能拷贝入一个 value_type 的某种东西;若 x 成功插入容器,b 为 ture,若容器中已有元素与 x 关键字相同,b 为 false;p 指向关键字与 x 相同的元素
p2 = c.insert(p,x);x 是一个 value_type 或能拷贝入一个 value_type 的某种东西;p 提示从哪里开始查找关键字与 x 相同的元素;p2 指向关键字与 x 相同的元素
c.insert(b,e);对 [b,e) 的每个 p 执行 c.insert(*p)
c.insert({args});将 initializer_list args 中的每个元素都插入容器中;元素类型为 pair<key_type,mapped_type>
p = c.emplace(args);从 args 构造一个类型为 c 的 value_type 的对象,将它插入 c 中,p 指向该对象
p = c.emplace_hint(h,args);从 args 构造一个类型为 c 的 value_type 的对象,将它插入 c 中,p 指向该对象;h 为指向 c 中的迭代器,可能用来提示从哪里开始搜索存放新元素的位置
r = c.key_comp()r 是关键字比较对象的一个拷贝;只适用于有序容器
r = c.value_comp()r 是值比较对象的一个拷贝;只适用于有序容器
n = c.count(k)n 是关键字等于 k 的元素数目

无序关联容器

有序关联容器使用关键字类型的比较运算符(默认是<)进行查找。
无序关联容器使用哈希函数进行查找。
无序关联容器在存储上组织为一组桶,每个桶保存零个或多个元素。
无序关联容器使用一个哈希函数将元素映射到桶。
无序关联容器将具有一个特定哈希值的所有元素都保存在相同的桶中;如果容器允许重复关键字,所有具有相同关键字的元素也都会在同一个桶中。
为了访问容器中一个元素,容器首先计算元素的哈希值,它指出应该搜索哪个桶。

无序关联容器的构造函数

unordedred_map 的遍历顺序取决于插入顺序、哈希函数和装载因子。
特别是,元素的遍历顺序并不保证与其插入顺序一致。
默认情况下,unordedred_map<X> 用 hash<X> 计算哈希值,用 equal_to<X>(==运算符)比较关键字。
对于无序关联容器,其模板参数、构造函数以及默认值的组合,有一些固定模式:
此处,除了空 unordedred_map 的情况,n 都表示元素计数。

成员函数功能
unordedred_map m{n,hf,eql,a};构造 n 个桶的 m;哈希函数为 hf;相等性比较函数为 eql;分配器为 a;显示构造函数
unordedred_map m{n,hf,eql};unordedred_map m{n,hf,eql,allocator_type{}};显示构造函数
unordedred_map m{n,hf};unordedred_map m{n,hf,key_eq{}};显示构造函数
unordedred_map m{n};unordedred_map m{n,hasher{}};显示构造函数
unordedred_map m{};unordedred_map m{N};桶数 N 由具体实现定义;显示构造函数

此处,从[b,e)中获得初始元素;元素数目为[b,e)中的元素数目。

成员函数功能
unordedred_map m{b,e,n,hf,eql,a};构造 n 个桶的 m;初始元素来自于[b,e)间的元素;哈希函数为 hf;相等性比较函数为 eql;分配器为 a
unordedred_map m{b,e,n,hf,eql};unordedred_map m{b,e,n,hf,eql,allocator_type{}}
unordedred_map m{b,e,n,hf};unordedred_map m{b,e,n,hf,key_eq{}}
unordedred_map m{b,e,n};unordedred_map m{b,e,n,hasher{}}
unordedred_map m{b,e};unordedred_map m{b,e,N};桶数 N 由具体实现定义

此处,初始元素来自于一个初始化列表, unordedred_map 中的元素数目即为初始化列表中的元素数目。

成员函数功能
unordedred_map m{{elem},n,hf,eql,a};构造 n 个桶的 m;初始元素来自于 initializer_list;哈希函数为 hf;相等性比较函数为 eql;分配器为 a
unordedred_map m{{elem},n,hf,eql};unordedred_map m{{elem},n,hf,eql,allocator_type{}}
unordedred_map m{{elem},n,hf};unordedred_map m{{elem},n,hf,key_eq{}}
unordedred_map m{{elem},n};unordedred_map m{{elem},n,hasher{}}
unordedred_map m{{elem}};unordedred_map m{{elem},N};桶数 N 由具体实现定义

装载因子和桶

哈希策略

无序关联容器的装载因子定义已用空间的比例。
例如:若 capacity() 为 100,size() 为 30,则 load_factor() 为 0.3。
需要通过实验来为给定的一组元素和一个特定哈希函数寻找一个合适的装载因子,但 0.7 通常是一个好选择。

成员函数功能
h = c.hash_function()h 是 c 的哈希函数
eq = c.key_eq()eq 是 c 的相等检测函数
d = c.load_factor()d 是元素数除以桶数:double(c.size())/c.bucket_count();不抛出异常
d = c.max_load_factor()d 是 c 的最大装载因子;不抛出异常
c.max_load_factor(d)将 c 的最大装载因子设置为 d;若 c 的最大装载因子已经接近其最大装载因子,c 将改变哈希大小(增加桶数)
c.rehash(n)令 c 的桶数大于等于 n
c.reserve(k)留出能容纳 n 个表项的空间(考虑装载因子):c.rehash(ceil(n/c.max_load_factor()))

这些成员函数允许我们查询容器的状态以及在必要时强制容器进行重组。

桶接口
成员函数功能
n = c.bucket_count()n 是 c 中的桶数(哈希表大小);不抛出异常
n = c.max_bucket_count()n 是一个桶中的最大元数;不抛出异常
n = c.bucket_size (n)m 为第 n 个桶中的元素数
i = c.bucket (k)关键字为 k 的元素在第 i 个桶中
桶迭代
成员功能
local_iterrator可以用来访问桶中元素的迭代器类型
const_local_iterrator桶迭代器的 const 版本
p = c.begin(n)桶 n 的首元素迭代器
p = c.end(n)桶 n 的尾后迭代器
p = c.cbegin(n)桶 n 的首元素迭代器,返回 const_local_iterrator
p = c.cend(n)桶 n 的尾后迭代器,返回 const_local_iterrator
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

往昔的恒纳兰那

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值