关联容器(associative container)是对容器概念的一种改进.

关联容器将值与键关联在一起,并使用键来查找值。

例:值可以是表示员工信息(如姓名,电话,地址,性别,健康计划等)的结构体,而键可以是唯一的员工编号。为获取员工信息,程序使用键查找员工结构。

关联容器的优点:

提供了对元素的快速访问,也允许插入新元素,但不能指定元素的插入位置(原因是关联容器通常用于确定数据放置位置的算法,以便能快速检索信息)

关联容器通常是使用某种树实现的。树是一种数据结构,其根节点链接到一个或两个节点,而这些节点又链接到一个或两个节点,从而形成分支结构。

像链表一样,节点使得添加或删除数据比较简单,但对于链表,树的查找速度更快。

STL提供了4种关联容器:

set :

multiset : 这两种头文件都为set

map :

multimap :这两种头文件都为map


set:是其中最简单的关联容器,其值类型与键相同,键是唯一的,这意味着集合中不会有多个相同的键。对于set来说,其值就是键

multiset:类似于set,只是可能有多个值的键相同。

map:其值与键的类型不同,键是唯一的,每个键只对应一个值。

multimap:类似于map,只是一个键可以与多个值关联。


1)set示例:

set<string> A; //可以带第二个参数(可 选),默认情况使用模板less<>

set<string, less<string> > A; // older implementation

#include <iostream>
#include <set>
#include <string>

int main()
{
	using namespace std;
	const int N = 6;
	string s1[N] = {"buffoon", "thinkers", "for", "heavy", "can", "for"};//注意这里有两个相同的for
	set<string> A(s1, s1 + N);//set不仅去重,还按升序排序
	ostream_iterator<string, char> out(cout, " ");
	copy(A.begin(), A.end(), out);//buffoon can for heavy thinkers 
	cout << endl;
	return 0;
}

数学还为set定义了一些标准操作,如

并集:包含两个集合合并后的内容。如果两个集合包含相同的值,则这个值在并集中只出现一次,因为set的键是唯一的。

    set_union()函数接受5个迭代器参数,前2个迭代器定义了第一个集合的区间,后2个迭代器定义了第二个集合的区间,最后一个迭代器是输出迭代器,指出将结果集合复             制到什么位置。

    set_union(A.begin(), A.end(), B.begin(), B.end(), ostream_iterator<string,char>(cout, " "));//显示

       set_union(A.begin(), A.end(), B.begin(), B.end(), C.begin());//放入C集合中,这种做法是错误的

    原因有两个:

            1)首先,关联集合将键看作常量,所以C.begin()返回的迭代器是常量迭代器,不能用作输出迭代器。

            2)其次,与copy()相似,set_union()将覆盖容器中已有的数据,并要求容器有足够的空间来容纳新信息。C是空的,不能满足这种要求。

            但模板insert_iterator可以解决这两个问题,它可以将复制转换为插入。另外,它可以模拟输出迭代器,可以用它将信息写入容器。

            所以这里可以创建一个匿名insert_iterator,将信息复制给C,如:

    set_union(A.begin(), A.end(), B.begin(), B.end(), insert_iterator<set<string> >(C, C.begin()));

交集:包含两个集合都有的元素。

            set_intersection(),用法与set_union类似

差集:两个集合的差是第一个集合减去两个集合都有的元素。

            set_difference(),用法与set_union类似

STL提供了支持这些操作的算法。它们是通用函数,并非只用于set对象。

所以set对象都自动满足这些算法的先决条件,即容器是经过排序的。


两个有用的set方法:

lower_bound():将键作为参数返回一个迭代器,该迭代器指向集合中第一个不小于键参数的成员。

upper_bound():将键作为参数返回一个迭代器,该迭代器指向集合中第一个大于键参数的成员。

作用:可以获取一个包含合集的区间。


set中因为会自动排序,所以只需指定插入的信息,不需指定插入位置。

关于上述说明的实例应用:

#include <iostream>
#include <set>       // set
#include <string>    // string
#include <algorithm> //set_union
#include <iterator>
int main()
{
	using namespace std;
	const int N = 6;
	string s1[N] = {"buffoon", "thinkers", "for", "heavy", "can", "for"};//注意这里有两个相同的for
	string s2[N] = {"metal", "any", "food", "elegant", "deliver", "for"};

	set<string> A(s1, s1 + N);//set不仅去重,还按升序排序
	set<string> B(s2, s2 + N);
	ostream_iterator<string, char> out(cout, " ");
	
	cout << "Set A:\n";
	copy(A.begin(), A.end(), out);//buffoon can for heavy thinkers 
	cout << endl;

	cout << "Set B:\n";
	copy(B.begin(), B.end(), out);//any deliver elegant food for metal
	cout << endl;

	cout << "Union of A and B:\n";
	set_union(A.begin(), A.end(), B.begin(), B.end(), out);
	//any buffoon can deliver elegant food for heavy metal thinkers
	cout << endl;

	cout << "Intersection of A and B:\n";
	set_intersection(A.begin(), A.end(), B.begin(), B.end(), out); //for
	cout << endl;

	cout << "Difference of A and B:\n";
	set_difference(A.begin(), A.end(), B.begin(), B.end(), out);//buffoon can heavy thinkers
	cout << endl;

	set<string> C;
	cout << "Set C:\n";
	set_union(A.begin(), A.end(), B.begin(), B.end(),
		insert_iterator<set<string> >(C, C.begin()));//将A与B的并集放入C集合中
	copy(C.begin(), C.end(), out); 
	//any buffoon can deliver elegant food for heavy metal thinkers
	cout << endl;

	string s3("grungy");
	C.insert(s3);
	cout << "Set C After insert s3:\n";
	copy(C.begin(), C.end(), out);
	//any buffoon can deliver elegant food for grungy heavy metal thinkers
	cout << endl;

	cout << "Showing a range:\n";
	copy(C.lower_bound("ghost"), C.upper_bound("spook"), out);
	//grungy heavy metal
	cout << endl;
	return 0;
}

multimap示例:
multimap<int, string> codes; //#1

第三个参数是可选的,指出用于对键进行排序的比较函数或对象。在默认情况下,使用模板less(),该函数将键类型用作模板参数类型。

实际的值将键类型与数据类型结合为一对,pair<class T, class U>,

如果keytype是键类型,datatype是存储的数据类型,则值类型为pair<const keytype, datatype>,  如#1处,codes的对象的值的类型为pair<const int, string>;

假设用区号作为键来存储城市名,则

pair<const  int, string> item(213, "Los Angeles"); //声明一个
codes.insert(item); //插入

也可以创建匿名对象来写:

codes.insert(pair<cosnt int, string> (213, "Los Angeles"));

因为数据项是按键排序的,所以不用指出插入位置。

访问里面的内容:

cout << item.first << " " << item.second << endl;

first表示键值,second表示数据信息。

equal_range()用键作为参数,且返回两个迭代器,它们返回的区间与该键匹配。为返回这两个值,该方法将封装在一个pair对象中,这里pair的两个模板参数都是迭代器

 如打印codes对象中区号为718的所有城市:

pair<multimap<KeyType, string>::iterator, multimap<KeyType, string>::iterator> range = codes.equal_range(718);
std::multimap<KeyType, string>::iterator it;
for (it = range.first; it != range.second; ++it)
	cout << (*it).second << endl;

如用C++11的自动推断功能,则:

auto range =  = codes.equal_range(718);
for (auto it = range.first; it != range.second; ++it)
	cout << (*it).second << endl;

也可使用typedef 来简化代码;

#include <iostream>
#include <string>
#include <map>
#include <algorithm>

typedef int KeyType;
typedef std::pair<const KeyType, std::string> Pair;
typedef std::multimap<KeyType, std::string> MapCode;

int main()
{
	using namespace std;
	MapCode codes;

	codes.insert(Pair(415, "San Francisco"));
	codes.insert(Pair(510, "Oakland"));
	codes.insert(Pair(718, "Brooklyn"));
	codes.insert(Pair(718, "Staten Island"));
	codes.insert(Pair(415, "San Rafael"));
	codes.insert(Pair(510, "Berkeley"));
	codes.insert(Pair(510, "Carry"));
	
	// count指出有几个键相同的值
	cout << "key 415: " << codes.count(415) << endl; // 2
	cout << "key 718: " << codes.count(718) << endl; // 2
	cout << "key 510: " << codes.count(510) << endl; // 3
	
	cout << "All codes:\n";
	MapCode::iterator it;
	for (it = codes.begin(); it != codes.end(); ++it)
		cout << (*it).first << " " << (*it).second << endl;
	// All codes:
	// 415 San Francisco
	// 415 San Rafael
	// 510 Oakland 不知道为什么它会在这里???
	// 510 Berkeley
	// 510 Carry
	// 718 Brooklyn
	// 718 Staten Island

   //找出所有键值相同的
	pair<MapCode::iterator, MapCode::iterator> range = codes.equal_range(718);
	cout << "key 718:\n";
	for (it = range.first; it != range.second; ++it)
		cout << (*it).second << endl;
	// key 718:
	// Brooklyn
	// Staten Island
	return 0;
}

无序关联容器(C++11)

无序关联容器也是对容器概念的另一种改进。与关联容器一样。

底层区别:关联容器是树结构,无序关联容器是基于数据结构哈希表的,其目的在于提高添加与删除元素的速度以及提高查找算法的效率。

有4种无序关联窗口:

1)unordered_set

2)unordered_multiset

3)unordered_map

4)unordered_multimap







Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐