比如我们要定义一个下标从 -1000 到 1000 (闭区间)的 int 类型数组,我们可以这样写:
int *a = new int[2001] + 1000;
解释:下标从 -1000 到 1000 (闭区间)意味着有 2001 个元素,我们先 new 一个长度为 2001 的数组,然后加上 1000 的偏移量,这样 a 指向的实际上是原数组的第 1001 个元素。
如果要定义很多这样的数组,我们不妨定义一个宏:
#define def_array(type, name, begin, end) type *name = new type[(end) - (begin) + 1] - (begin)
然后就可以用这个宏方便地定义这样的数组了,当然仅限于一维数组:
def_array(int, a, -1000, 1000);
STL 中比较常用的容器有:vector
、map
、queue
、stack
、priority_queue
和set
。因为它们申请内存时都是动态申请,在 OI 中相对较慢,所以我们只在对性能要求不高的时候使用它们。
具体的用途用法以后补充,可以先自己去找一下资料。
思考一下类似于链表、字符串、数组这样的线性数据结构,我们最常用的操作是什么?
答案当然是遍历(别说这个你都想不到-_-
)。
并且当我们遍历链表、字符串、数组时,我们写的代码是不同的。
为什么?因为它们类型不一样。比如说 std::vector<int>
和int []
是不同的类型,char []
和 std::string
也是不同的类型。
但是我们有的时候要对它们做相同的操作,比如说每个元素加上一个值,把所有元素倒过来等等等等。
难道我们要为不同的类型都写一个专门的函数吗?但是要执行的操作明明是一样的。真是够了……
而迭代器就是为了解决这种问题的,它为顺序访问提供了一个统一的接口,使我们不用担心容器原本的类型。
那么统一的接口是什么呢?
迭代器好比一个游标,指向数组中的某个元素,我们可以向前或向后移动这个游标,使其指向前一个或者后一个元素。
如果 ivec
是一个元素类型为 int
的 vector
:
std::vector<int> ivec;
我们可以这样获取它的首迭代器和尾迭代器:
std::vector<int>::iterator head = ivec.begin();
std::vector<int>::iterator tail = ivec.end();
std::vector<int>::iterator
这一串长长的东西是什么?
这就是std::vector<int>
的迭代器的类型,就是在原本的类型名后面加了个::iterator
而已。STL 中其他的容器也是这样。
而ivec.begin()
就是获取ivec
的首迭代器,ivec.end()
就是获取 ivec
的尾迭代器。同样,STL 中其他的容器也是这样。
头迭代器是指向第一个元素,那尾迭代器是指向最后一个元素吗?
不是。尾迭代器是指向最后一个元素后面的元素,是一个不存在的元素,它的作用只是作为一个标志,标志迭代器迭代的终点。这里你可能不明白,没事,我们看代码体会一下:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> ivec;
for (int i = 0; i < 10; i++) {
ivec.push_back(i);
}
for (vector<int>::iterator it = ivec.begin(); it != ivec.end(); ++it) {
cout << *it << endl;
}
return 0;
}
这段代码它的输出就是从 0 到 9。
先写到这里。 To be continued.