目录
第二章 STL
前言
string/vector/list 容器
stack/queue/priotity_queue 容器适配器
优先级队列
dequeue 双端队列 可以用于实现栈和队列
缺点
1 大量的频繁operator[]的效率低
2 迭代器的遍历相对复杂,效率也有一些影响
容器适配器 都不支持迭代器遍历,因为他们通常都包含一些特殊性质
stack简介
1.stack在C++中是一种容器适配器,具有后进先出的操作,其删除只能从容器的一端进行元素的插入与提取操作。
2.stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
3.stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
- empty:判空操作
- back:获取尾部元素操作
- push_back:尾部插入元素操作
- pop_back:尾部删除元素操作
4.标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。
基本使用
函数声明 接口说明
empty() 检测栈是否为空,是返回true,否则返回false
size() 返回栈中有效元素的个数
top() 返回队尾元素的引用
push() 在栈顶将元素val入队列
pop() 将栈顶元素出队列
// #include<stack> // 引入头文件
stack<int> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
// 没有迭代器
while (!st.empty())
{
cout << st.top() << ' ';
st.pop();
}
cout << endl;
// stack < int, list<int> > s;
模拟实现
namespace hek1
{
template<class T,class Container>
class stack
{
public:
void push(const T& x){return _con.push_back(x);}
void pop(){return _con.pop_back();}
size_t size(){return _con.size();}
bool empty(){return _con.empty();}
T& top(){return _con.back();}
private:
Container _con;
};
}
queue简介
1.队列在C++中是一种容器适配器,具有先进先出的操作,其中从容器一端插入元素,另一端提取元素。
2.队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
3.底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
empty:检测队列是否为空
size:返回队列中有效元素的个数
front:返回队头元素的引用
back:返回队尾元素的引用
push_back:在队列尾部入队列
pop_front:在队列头部出队列
4.标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。vector不行是因为没有实现pop_front
基本使用
函数名 接口说明
queue() 构造空的队列
empty() 检测队列是否为空,是返回true,否则返回false
size() 返回队列中有效元素的个数
front() 返回队头元素的引用
back() 返回队尾元素的引用
push() 在队尾将元素val入队列
pop() 将队头元素出队列
// #include<queue>
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
// 没有迭代器
while (!q.empty())
{
cout << q.front() << ' ';
q.pop();
}
cout << endl;
// queue < int, list<int> > s; 这样也可以
模拟实现
namespace hek2
{
template<class T, class Container>
class queue
{
public:
void push(const T& x) { return _con.push_back(x); }
void pop() { return _con.pop_front(); }
size_t size() { return _con.size(); }
bool empty() { return _con.empty(); }
T& front() { return _con.front(); }
T& back() { return _con.back(); }
private:
Container _con;
};
}
deque简介
双端队列是官方指定的底层容器,其结构上的特殊设计决定了它只适合于头尾操作需求高的场景:双端队列 = vector
+ list
,设计时就想汲取二者的优点(下标随机访问 + 极致的空间使用),但鱼和熊掌不可兼得,在复杂结构的加持之下,双端队列趋于中庸,无法做到 vector
和 list
那样极致,因此实际中也很少使用,比较适合当作适配器的底层容器
双端队列的数据结构:list
+ vector
- 利用
list
构造出一个map
作为主控数组(通过链式结构链接),数组中元素为数组指针 - 利用
vector
构造出大小为N
的小数组(缓冲区),这些小数组才是双端队列存储元素的地方
注意:此处的 map
并非是容器 map
,仅仅是名字相同而已
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示
数据相关
头插尾插的时候,不需要挪动数据。而随机访问,比如每个数组的容量为10,我们要找20,那么就先计算出20是在第几个数组中(用20/10 = 2),然后再算出在这个数组的哪里(找到是在第二个数组后,再20%10 = 0,在第0个下标上)。
迭代器
这个迭代器是一个随机迭代器,因此可以使用 std::sort
- 无论是
deque
还是list
,直接排序的效率都不如借助vector
间接排序效率高
deque
的缺点
- 中间位置插入删除比较麻烦,可以令小数组长度不同解决问题,不过此时影响随机访问效率
- 结构设计复杂,且不如
vector
和list
极致 - 致命缺陷:不适合遍历,迭代器需要频繁检测是否移动某段小空间的边界,效率很低
凑巧的是,栈和队列
可以完美避开所有缺陷,全面汲取其优点,因此 双端队列 为容器适配器的默认底层容器