零、pair类模板

pair与python中字典的键值对非常像。在C++中,pair的定义是:

1
2
3
4
5
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的集合中的元素都是不能重复的。

使用multiset必须包含头文件set。mutilset类模板是这样定义的:

1
2
template <typename Key, typename Pred=less<Key>, typename B=allocator<Key>>
class multiset{...};

第一个参数:说明元素类型。

第二个参数:说明排序规则,实例化后的Pred可以是函数对象类,也可以是函数指针,这里默认的是less函数对象,定义如下:

1
2
3
4
5
6
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;

下面来看看我是怎么用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <set>
using namespace std;

class A{
private:
int n;
public:
A(int _n):n(_n){}
friend class MyLess;
//需要对<进行重载,因为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;
}

注意,容器内的类型若是类,那么许多符号需要进行重载:<<、<、>、==、······等等。

multiset自带的成员函数与vector和list很多重合,但是还有一些自己独有的,这里列举一下几个常用的:

m.insert(n); //将n插入容器中,返回其迭代器。

m.find(first, last, n); //在容器[first, last)查找n,找到返回其迭代器,找不到返回last的迭代器。first和last可以省略

m.erase(i); //删除i指向的元素,并返回后面一个元素的迭代器

m.erase(first, end); //删除区间[first, end),并返回迭代器last

m1.count(n); // 统计多少个元素的值与n相等

m.lower_bound(n); //查找一个最大位置it,使得[begin,it)中所有元素的关键字都比n小,返回值是迭代器

m.upper_bound(n); //查找一个最小位置it,使得[it, end)中所有元素的关键字都比n大,返回值是迭代器

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类模板是这样定义的:

1
2
3
4
5
6
template <typename Key, typename T, typename Pred=less<Key>, typename B=allocator<Key>>
class map{
...
typedef pair<const Key, T> value_type;
...
};

第一、二个参数:说明元素类型。

第三个参数:说明排序规则,实例化后的Pred可以是函数对象类,也可以是函数指针。这里默认的是less函数对象。

2.实例

注意一个问题:输出map的时候,需要重载<<,这是因为,map的元素是pair对象,如果不进行重载<<,那么编译将出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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;
}

输出结果:

1
2
3
4
5
a: 1
b: 2
c: 3
d: 4
e: 5

3.常用成员函数

与set中的成员函数是一样的,这里不再阐述。不过insert中的参数必须是pair模板类对象。例如上面的程序。

注意,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=已有元素的迭代器。