C++:关联容器(set,multiset,map,multimap)
关联容器(associative container)是对容器概念的一种改进.关联容器将值与键关联在一起,并使用键来查找值。例:值可以是表示员工信息(如姓名,电话,地址,性别,健康计划等)的结构体,而键可以是唯一的员工编号。为获取员工信息,程序使用键查找员工结构。关联容器的优点:提供了对元素的快速访问,也允许插入新元素,但不能指定元素的插入位置(原因是关联容器通常用于确定数据放置位
关联容器(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
更多推荐
所有评论(0)