发现每个数据结构都单独成文会显得文章有点多,且内容不充实。于是决定分组放了,下一部分就是『集合、映射和栈了』,基础数据结构复习完毕后,刷题就提上日程。在进入之前,先扯一点STL的东西。
STL
Standard template library,C++标准库的重要组成部分,由这几部分组成:
- 容器:保存一组数据,各种各样的类型,数据个体是元素。
- 迭代器:一种泛型指针,和容器共同使用,访问容器中的元素(非下标)。是将*, ++, ->进行了重载的类模板,不同容器的迭代器不一样。
- 算法:操作容器里面数据的一种方法,和具体容器无关。查找,排序,替换等。
- 函数对象
- 空间分配器
容器分类
- 顺序容器:线性数据结构,
vector, list, deque
,多个元素的有序集合,元素有前有后
- 关联容器:非线性数据结构,
map, set
,多个元素的无序集合,元素无前无后,用于存储键值对
- 容器适配器:依赖顺序容器的受限版本,处理特殊情况。
stack, queue, priority_queue
。
- vector,直接访问任意元素,快速插入、删除尾部元素
- deque,直接访问任意元素,快速插入、删除头部和尾部元素,但开销大
- list,快速插入删除任意位置的元素,不能使用数组下标,所以没办法访问任意位置元素
- set,快速查询元素,无重复关键字
- multiset,允许重复关键字
- map,键值对,不允许重复关键字,使用关键字快速查询元素
- multimap,允许重复关键字
- stack,后进先出
- queue,先进先出
- priority_queue,元素出队顺序取决于优先级的队列
所有容器的共同函数:
- 构造函数
- 拷贝函数
- empty()判空
- size()元素数目
- operator()=将容器复制
- >, <, ==, !=的判断
一级容器的共同函数(顺序容器+关联容器):
- a.swap(b)交换
- a.max_size()最大容纳的元素数量
- clear()删除
- begin()返回容器首元素的迭代器
- end()返回容器尾元素之后位置的迭代器
- rbegin()容器最后位置的迭代器,逆序遍历
- rend()容器首元素之前位置的迭代器
- erase(begin, end)删除[begin, end-1]位置的元素,两者都是迭代器
顺序容器
- 随机迭代器:vector,deque(双端队列)
- 双向迭代器:list
- vector:一端操作的数组,满了之后申请新的数组,并拷贝原有数组
- deque:双端操作,先入先出,内部有多个数组容纳新的元素
- list:若干节点,节点有两个指针,有前驱和后继,插入和删除快
共同函数:
- assign(n, elem)元素做n份拷贝,覆盖原有内容
- assign(beg, end)迭代器[beg, end)之间的元素赋值给当前容器
- push_back尾部添加
- pop_back删除尾部
- front返回首部
- back返回尾部
- insert(pos, elem)元素插入到指定位置
关联容器
使用key快速存取元素,元素按默认规则进行排序。共同函数:
- find(key)搜索容器中具有key的元素,返回指向的迭代器,没找到返回指向容器最后一个元素后面的迭代器
- lower_bound(key)搜索具有key的第一个元素,返回指向元素的迭代器
- upper_bound(key)搜索具有key的最后一个元素,返回指向元素之后位置的迭代器
- count(key)具有key元素的数量
vector
相对于array
数组大小固定不能动态变化的缺陷,vector
能很好的避免这一点。同array
,使用时需要引入头文件,并在std
空间中声明。
声明与初始化
1 2 3 4 5 6 7 8 9 10
| #include <vector> using std::vector; ... double values[] {1, 2, 3, 4, 5, 6, 7};
vector<double> v(values, values + 7);
v.resize(100);
vector<int> v5 = { 1,2,3,4,5 };
|
其他操作
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
| for (int i = 0; i < v.size(); i++){ cout << v[i] << " "; } cout << endl;
v.assign(3, 11.8); for (auto i : v){ cout << i << " "; } cout << endl;
v.at(0) = 22.4; for (auto i : v){ cout << i << " "; } cout << endl;
vector<double>::iterator itr = v.begin();
v.insert(itr + 1, 321); for (auto i : v){ cout << i << " "; } cout << endl;
itr = v.begin();
v.erase(itr, itr + 2); for (auto i : v){ cout << i << " "; } cout << endl;
v.clear(); if (v.empty()){ cout << "True"; }
cout << v5.front() << endl;
cout << v5.back() << endl;
v5.pop_back();
v5.push_back(6);
v5.erase(v5.begin() + 3);
v5.insert(v5.begin() + 1, 7, 8);
v5.size();
|
也许你会有疑问,insert
可以在队首插入元素,为啥vector
还是单端操作的容器呢?因为push_back
只会在队尾追加元素,不到迫不得已vector
是不会在内存中移动的,除非申请新的空间或删除大部分元素。而insert
会强制vector
去移动元素,这就造成了大量的内存开销。这里脑补『数据结构』中顺序结构插入元素时,其它元素的移动吧。或者这里的回答。
deque
de表示单词double,que取自单词queue,表示这是可以双端操作的队列。因其开销较大,所以一般使用vector
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <deque> double values[] {1, 2, 3, 4, 5, 6, 7};
deque<double> q(values, values + 7);
q.push_front(0); for (auto i : q){ cout << i << " "; } cout << endl;
q.pop_front(); q.pop_back(); for (auto i : q){ cout << i << " "; } cout << endl;
|
list
list
是链表,众所周知链式结构不支持随机访问。
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
| #include <iostream> #include <list>
using namespace std;
int main(){
int values[] = {1, 2, 3, 4}; list<int> l(values, values + 4);
list<int>::iterator p; for (p = l.begin(); p != l.end(); p++){ cout << *p << " "; } cout << endl;
list<int>::iterator itr = l.begin(); itr ++; l.insert(itr, 90); for (p = l.begin(); p != l.end(); p++){ cout << *p << " "; } cout << endl; cout << *itr << endl;
return 0; }
|
list特有
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
| l.sort(); l.reverse(); for (p = l.begin(); p != l.end(); p++){ cout << *p << " "; } cout << endl;
int values1[] = {6, 7, 8, 9}; list<int> l1(values1, values1 + 4);
l.sort();
l.merge(l1); for (p = l.begin(); p != l.end(); p++){ cout << *p << " "; } cout << endl;
l.remove(90);
p = l.end(); l1.push_back(32);
l.splice(p, l1); for (p = l.begin(); p != l.end(); p++){ cout << *p << " "; } cout << endl;
|