以下介绍的都属于关联容器,关联容器是用平衡二叉树(红黑树)实现的,本篇不讨论红黑树。
零、pair类模板
pair与python中字典的键值对非常像。在C++中,pair的定义是:
template <typename T1, typename T2>
struct pair{
T1 first;
T2 second;
...下面都是各种构造函数重载版本,不写了
可以不写参数:pair<string, double> p
可以写参数(常用):pair<string, double> p1("xiaoming", 78)
可以这样初始化:pair<string, double> p2(pair<string, double>("xiaoming", 78))
,当这样写的时候,其实是用了定义中的函数模板,参数必须是pair模板类对象的引用。
也可以这样初始化:pair<string, double> p3 = make_pair("xiaoming", 78)
访问pair对象的“键”(Key):p.first
访问pair对象的“值”(Value):p.second
一、multiset和set
C++的set类似于python中的集合。只不过,python的集合中的元素都是不能重复的。
1.定义
使用multiset必须包含头文件set。mutilset类模板是这样定义的:
template <typename Key, typename Pred=less<Key>, typename B=allocator<Key>>
class multiset{...};
第一个参数:说明元素类型。
第二个参数:说明排序规则,实例化后的Pred可以是函数对象类,也可以是函数指针。这里默认的是less函数对象,定义如下:
template <typename T>
class less
{
bool operator ()(const T &x, const T &y)const
{ return x<y; }
};
事实上在STL中自带一些函数对象类模板,less是其中一个。STL中自带的函数对象类模板,可以书上查,可以网上查,这里不一一列举。
第三个参数极少用到,不写。
定义一个multiset容器对象:multiset <int> a;
,等价于multiset <int, less<int>, allocator<int>> a;
2.实例
下面来看看我是怎么用的:
#include <iostream>
#include <set>
using namespace std;
class A{
private:
int n;
public:
A(int _n):n(_n){}
friend class MyLess;
friend bool MyLess(const A &a1, const A &a2);
//需要对<进行重载,因为less函数对象类使用了<,会出现这样的语句a1<a2
//若不进行解释,则编译出错
friend bool operator < (const A &a1, const A &a2)
{
return a1.n < a2.n;
}
//需要对<<进行重载
friend ostream &operator << (ostream &o, const A &a)
{
o << a.n;
return o;
}
};
class MyLess{ //用函数对象自定义了排序规则:按照个位数从小到大排序
public:
bool operator () (const A &a1, const A &a2)
{
return (a1.n % 10)<(a2.n % 10);
}
};
//这里cout<<*first其实相当于是cout<<A,直接输出一个对象,
//所以,若不对<<进行重载,cout<<*first就无法解释了,编译会出错
template <typename T>
void Print(T first, T end)
{
for(; first != end; ++first) cout<<*first<<" ";
cout<<endl;
}
int main()
{
const int SIZE = 7;
A a[SIZE] = {4, 22, 19, 8, 33, 40, 67};
multiset<A, less<A>> m1;//等价于multiset<A>,默认使用STL的less函数对象类模板
multiset<A, MyLess> m2;//用我们自己定义的函数对象
m1.insert(a, a+SIZE);
Print(m1.begin(), m1.end());//4 8 19 22 33 40 67
m2.insert(a, a+SIZE);
Print(m2.begin(), m2.end());//40 22 33 4 67 8 19
return 0;
}
注意,容器内的类型若是类,那么许多符号需要进行重载:<<、<、>、==、······等等。
3.常用成员函数
multiset自带的成员函数与vector和list很多重合,但是还有一些自己独有的,这里列举几个常用的:
m.insert(n);
//将n插入容器中,返回其迭代器
m.erase(i);
//删除i指向的元素,并返回后面一个元素的迭代器
m.erase(first, end);
//删除区间[first, end),并返回迭代器last
m.find(first, last, n);
//在容器[first, last)查找n,找到返回其迭代器,找不到返回last的迭代器。first和last可以省略
m.count(n);
// 统计多少个元素的值与n相等
m.lower_bound(n);
//查找一个最大位置it,使得[begin,it)中所有元素的关键字都比n小,返回值是迭代器it
m.upper_bound(n);
//查找一个最小位置it,使得[it, end)中所有元素的关键字都比n大,返回值是迭代器it
m.equal_range(n);
//同时求lower_bound和upper_bound,返回的是一个pair模板类对象x,x.first和x.second都是迭代器类型,前者是lower_bound,后者是upper_bound
注意,multiset和set中的find和count不是用“==”来比较相等的,而是遵循这样的原则:如果“x比y小”且“y比x小”同时为假,就认为x和y相等。
4.关于set
set和multiset类似,但是set不能有重复的元素。由于不能重复元素,所以set的insert函数与multiset的不同:
m.insert(n);
//将n插入容器中,返回pair模板类对象x,若x.second=true,则说明插入成功,x.first=被插入元素的迭代器;若x.second=false,则说明已有该元素,x.first=已有元素的迭代器。
二、multimap和map
C++的map类似于python中的字典。
1.定义
使用multiset必须包含头文件set。mutilset类模板是这样定义的:
template <typename Key, typename T, typename Pred=less<Key>, typename B=allocator<Key>>
class map{
...
typedef pair<const Key, T> value_type;
...
};
第一、二个参数:说明元素类型。
第三个参数:说明排序规则,实例化后的Pred可以是函数对象类,也可以是函数指针。这里默认的是less函数对象。
multimap的元素都是pair模板类的对象,first是关键字(key),second是值(value)。
2.实例
注意一个问题:输出map的时候,需要重载<<,这是因为,map的元素是pair对象,如果不进行重载<<,那么编译将出错。
#include<iostream>
#include<map>
using namespace std;
template <typename T1, typename T2>
ostream &operator<< (ostream &o, const pair<T1, T2> &p)
{
o<<p.first<<": "<<p.second;
}
template <typename T>
void Print(T first, T end)
{
for(; first != end; ++first) cout<<*first<<endl;
}
int main()
{
typedef map<char, int, less<char>> MAP;
MAP m;
for(char i='a'; i<='e'; ++i)
m.insert(MAP::value_type(i, i-96));
Print(m.begin(), m.end());
return 0;
}
输出结果:
a: 1
b: 2
c: 3
d: 4
e: 5
我这个程序写的是map哦!
3.常用成员函数
与set中的成员函数是一样的,这里不再阐述。不过insert中的参数必须是pair模板类对象。例如上面的程序:m.insert(MAP::value_type(i, i-96));
注意,multimap和map中的find和count不是用“==”来比较相等的,而是遵循这样的原则:如果“x比y小”且“y比x小”同时为假,就认为x和y相等。
4.关于map
set和multiset类似,但是set不能有重复的元素。
除了multimap的所有特点,map还有成员函数:T &operator[] (Key k)
。例如上面的程序中,cout<<m['e']<<endl;
将输出5,注意,这个方框里的是关键字(key)。
与set类似,map的insert函数也有这样的特性:
m.insert(n);
//将n插入容器中,返回pair模板类对象x,若x.second=true,则说明插入成功,x.first=被插入元素的迭代器;若x.second=false,则说明已有该元素,x.first=已有元素的迭代器。
下一篇可能涉及容器适配器的内容。