容器适配器
首先我们要知道容器适配器是干啥的。
我们可以简单的将容器适配器当做一个接口装置。有的电脑上没有数据转接口,但是有usb接口,这是我们没必要重新买一个电脑,我们可以做一个usb数据转接线。而这根数据转接线就类似于适配器一样。在C++中,我们已经有了(vector、list、deque等容器)插入、删除、迭代器就电脑usb接口,当我们想要实现栈的操作时,我么没有必要再重新写新的数据结构,只需要将其接口进行重新的封装,相当于做了一根数据转接线。我们就可以将这个容器当做数据结构使用。
C++中定义了3种容器适配器,它们让容器提供的接口变成了我们常用的的3种数据结构,stack(栈)、queue(队列)、priority_queue(优先级对列)
- 栈:先进后出,我们可以想一下,在STL中,其底层应该一些序列式中容器,能够进行提供push_back(向栈顶插入元素) 、pop_back(删除栈顶元素)序列式容器都可以满足封装成栈的要求。
- 队列:先进先出。对pop_front(头删)、push_back(尾插)操作的序列式容器的封装,底层不可能是vector。
- 优先级队列:支持随机访问的功能,所以其底层结构只能为vector或duque。
stack(栈)接口
接口 | 接口说明 |
---|---|
empty() | 栈为空则返回真 |
pop() | 移除栈顶元素 |
push() | 在栈顶增加元素 |
size() | 返回栈中元素数目 |
top() | 返回栈顶元素 |
qeque(队列)接口
接口 | 接口说明 |
---|---|
empty() | 队列为空则返回真 |
front | 队首元素地址 |
back | 队尾元素地址 |
size() | 返回队列中元素数目 |
push() | 在队尾中插入元素 |
pop() | 队首元素出队 |
priority_queue(优先级对列)
接口 | 接口说明 |
---|---|
empty() | 如果优先队列为空,则返回真 |
pop() | 删除第一个元素 |
push() | 加入一个元素 |
size() | 返回优先队列中拥有的元素的个数 |
top() | 返回优先队列中有最高优先级的元素 |
我们观察一下,各个适配器的底层实现结构。
stack底层实现结构为deque
queue底层实现结构为deque
priority_queue底层实现结构为vector
为什么stack与queue底层为deque,priority_queue底层为vector?
deque于vector的区别
- 底层实现不同:为双端队列
deque | vector | |
---|---|---|
底层实现不同 | 双端队列 | 连续的空间 |
插入删除方式 | 头尾两端都可插入、删除 | 任意位置都可插入、删除 |
查找效率 | 支持随机访问,速度比较慢 | 支持随机访问,速度快 |
扩容机制 | deque在扩容不需要进行元素的搬移 | 扩容时需要进行元素的搬移 |
迭代器 | 对原生态指针的封装 | 原生态指针的别名 |
列举了几个重要的信息:我们现在了解到为啥stack、queue、priority_queue底层实现不同。
-
stack、queue不需要进行元素遍历,但是元素入栈、入队容易发生扩容操作,所以使用deque。在stack中元素增长时,deque比vector的效率高;queue中的元素增长时,deque不仅效率高,而且内存使用率高。
-
priority_queue需要对元素进行遍历,vector迭代器的速度相对于duque来说比较快。
stack模拟实现
由于直接使用stack容易于系统封装的stack发生冲突,解决办法 1.命名空间 2.类名修改,这里我使用第二中办法。
template<class T,class S = deque<T>>
class Stack
{
public:
Stack()
{
}
void push(const T& x)
{
_s.push_back(x);
}
void pop()
{
_s.pop_front();
}
T& top() //这块需要注意,由于栈是先进后出,这块于deque的刚好相反
{
return _s.back();
}
size_t size()
{
return _s.size();
}
bool empty()
{
return _s.empty();
}
private:
S _s;
};
queue模拟实现
namespace bai
{
template<class T, class D = deque<T>>
class queue
{
public:
queue()
{
}
void push(const T& data)
{
_d.push_back(data);
}
void pop()
{
_d.pop_front();
}
T& top()
{
return _d.front();
}
size_t size()
{
return _d.size();
}
bool empty()
{
return _d.empty();
}
T& back()
{
return _d.back();
}
private:
D _d;
};
}
priority_queue模拟实现
//模板参数:参数类型,底层实现结构,比较方式,默认为大顶堆 less:大堆 greater:小堆
template<class T, class Container = vector<T>,class Compare = less<T>>
class priority_queue
{
public:
priority_queue()
:con()
{
}
template<class iterator>
priority_queue(iterator first, iterator last)
: con(first,last)
{
int rear = con.size();
int node = ((rear - 2) >> 1);
for (; node >= 0; --node)
{
AdjustDown(node);
}
}
void push(const T& x)
{
con.push_back(x);
AdjustUp(con.size()-1);
}
void pop()
{
if (con.empty())
return;
swap(con.front(), con.back());
con.pop_back();
AdjustDown(0);
}
T& top()
{
return con.front();
}
bool empty()
{
return con.empty();
}
private:
void AdjustUp(int child_index)
{
int parent_index = (child_index - 1) >> 1;
while (child_index != 0)
{
if (comp(con[parent_index], con[child_index]))
{
swap(con[parent_index], con[child_index]);
child_index = parent_index;
parent_index = (child_index - 1) >> 1;
}
else
return;
}
}
void AdjustDown(int parent_index)
{
int child_index = parent_index * 2 + 1;
if (child_index < con.size() - 1 && comp(con[child_index], con[child_index+1]))
{
child_index += 1;
}
if (comp(con[parent_index], con[child_index]))
{
swap(con[parent_index], con[child_index]);
parent_index = child_index;
child_index = child_index * 2 + 1;
}
else
return;
}
Container con;
Compare comp;
};