概念
容器是什么
容器定义:
在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对象的指针,这种对象类型就叫做容器。
定义简单理解:
容器这种对象类型,从单词表面意思理解就是包含,包含了什么呢,是我们需要的存储结构的对象,这样的对象集成了特定存储结构和对存储的操作方式,可以重复利用这些代码方便编程。
在C++中的容器:
因为C++ 中处理容器是采用基于模板的方式,在C++ 中,标准模板库(STL )中包含了容器。STL中的容器提供了多种数据结构,例如栈,队列,数组等等。这些内容大家一定很熟悉。
通用容器的分类
STL 对定义的通用容器分三类:顺序性容器、关联式容器和容器适配器。
顺序性容器
顺序性容器是一种各元素之间有顺序关系的线性表。顺序性体现在哪里呢,这种顺序不依赖于元素的值,而是与元素加入容器的位置相对应。比如我们一次在某个位置加几个元素,这些元素就会按我们的操作顺序出现在那里。
关联式容器
关联式容器和顺序性容器不一样,关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,不像顺序性容器中例如队列、栈那样有着操作逻辑的顺序体现。尤其是地址上,它也不是顺序性的。虽然在关联式容器的操作中可以貌似“顺序地”获取元素,但是其实是借助的关联特点实现的。
关联式容器另一个显著的特点是它是以键值的方式来保存数据,就是说它能把关键字和值关联起来保存,而顺序性容器只能保存一种(可以认为它只保存关键字,也可以认为它只保存值。
容器适配器
容器适配器在C++的解释是:适配器是使一事物的行为类似于另一事物的行为的一种机制。它本身不能直接保存元素,它保存元素的机制是调用另一种顺序容器去实现,可以通俗理解成为现有的容器做接口。
STL 中提供的三种适配器可以由某一种顺序容器去实现。默认下stack 和queue 基于deque 容器实现,priority_queue 则基于vector 容器实现。可以自己指定顺序容器,但是要注意一个适配器不是可以由任何一个顺序容器来实现的。
容器的具体分类
按照上面的概念,我们对介绍过的容器进行性质分类,
顺序性容器:
- vector,相当于动态数组,可自动扩展
- deque,双端队列
- list,双向链表
关联式容器:
- set/multiset, 集合,多重集合
- map/multimap,键-值
容器适配器:
- stack,栈
- queue,队列
- priority_queue,优先级队列
函数速记
学习完这么多容器,可以明显感觉出这些容器的操作方法上有非常高的重复性,于是整理了下面的内容来对比速记。
1. 构造
对于顺序性容器,vector,deque,list来说,下面的方法通用
容器本身名字<数据类型> 定义的容器名; //无参构造
容器本身名字<数据类型> 定义的容器名1(定义的容器名2.begin(), 定义的容器名2.end()); //区间拷贝
容器本身名字<数据类型> 定义的容器名(10, 100); //指定元素个数的拷贝
容器本身名字<数据类型> 定义的容器名1(定义的容器名2); // 拷贝构造函数
//举例
vector<int> v1; //无参构造
vector<int> v2(v1.begin(), v1.end()); //区间拷贝
vector<int> v3(10, 100); //指定元素个数的拷贝
vector<int> v4(v3); // 拷贝构造函数
对于关联式容器和容器适配器,常用下两种
容器本身名字<数据类型> 定义的容器名; //无参构造
容器本身名字<数据类型> 定义的容器名1(定义的容器名2); // 拷贝构造函数
//举例
vector<int> v1; //无参构造
vector<int> v4(v3); // 拷贝构造函数
2. 赋值
对于顺序性容器,vector,deque,list来说,下面的方法通用
定义的容器名1 = 定义的容器名2; //重载运算符
assign(定义的容器名2.begin(), 定义的容器名2.end()); //区间赋值
assign(元素个数,元素); //指定元素个数的拷贝赋值
//举例
v2 = v1;
v3.assign(v1.begin(), v1.end());
v4.assign(10, 100);
对于关联式容器和容器适配器,就直接“=”
定义的容器名1 = 定义的容器名2; //重载运算符
//举例
v2 = v1;
3. 大小相关
通用接口
empty(); //判断容器是否为空
size(); //返回容器中元素的个数
顺序和关联有的
swap(st); //交换两个集合容器
顺序有的
resize(int num); //重新指定容器的长度为num
resize(int num,elem); //重新指定容器的长度为num,指定填充默认值
4. 插入删除
顺序式的区分,
vector,deque,list共有的
//尾插
v1.push_back(10);
//尾删
v1.pop_back();
//插入
v1.insert(v1.begin(), 100); // 插1个100
v1.insert(v1.begin(), 2, 1000); // 插2个1000
//删除
v1.erase(v1.begin());
//清空
v1.erase(v1.begin(), v1.end());
v1.clear();
deque,list才有的双端操作
//头插
L.push_front(100);
//头删
L.pop_front();
list有的
L.remove(10000); //删除匹配值
关联式的共性不大,暂时没有整理
容器适配器共有,但是存取的位置要结合特性,
栈是先进先出,栈顶添加栈顶移除
队列是先进后出,尾添加首移除
q.push(p1);
q.pop();
5. 存取相关
对于顺序式容器来讲,返回值
vector,deque和list通用,
q.front(); //返回容器中第一个数据元素
q.back(); //返回容器中最后一个数据元素
vector和deque有的,索引返回值
v1[i];
v1.at(i);
对于关联式容器来讲,查找统计通用
m.find(key);
m.count(key);
对于容器适配器来讲,区分非常好记
//栈只会返回top值
s.top(); //栈返回top值
// 队列会返回首尾值
q.front(); // 队列返回首值
q.back(); // 队列返回尾值