1. stack 和queue
stack 是栈,先入后出,只能从栈尾进行插入,栈尾进行输出。
queue是队列,元素先入先出,从队尾插入,队头出。
stack和queue的接口都类似,
区别仅仅是stack的栈顶元素用top,而queue的队头元素用front。
1.1 stack的底层
我们知道,vector类似于C语言学的数组,vector不适用于指定位置删除或增加,因为删除了之后需要移动后面的每个位置。
从stack和vector的接口看,栈就是一种特殊的vector。所以stack的底层用vector来实现。
而为了让栈适应所有类型的数据,就会用到模版。
namespace zzj
{
template<class T,class container = vector<T> >
class stack
{
public:
stack()
{};
void push(const T& x)
{
_C.push_back(x);
}
void pop()
{
_c.pop_back();
}
T& top()
{
return _c.back();
}
private:
container _c;
}
}
1.2 queue的底层实现
因为queue的接口存在头删和尾插,用vector封装太慢了,所以我们用list进行实现。
namespace zzj
{
template<class T,class container = list<T> >
class queu
{
public:
queue()
{};
void push(const T& x)
{
_C.push_back(x);
}
void pop()
{
_c.pop_front();
}
T& back()
{
return _c.back();
}
T& front ()
{
return _c.front();
}
private:
container _c;
}
}
2. priority_queue
优先级队列是一种容器适配器,将里面的元素全部进行按序排列。即按从大到小或从小到大进行排列。
默认是从大到小进行排列。
优先级队列的底部通常是vector。但是优先级队列只能检索第一个元素,即最大或最小的元素。类似于堆,因此底层我们可以通过堆来进行排列。
2.1 优先级队列的模拟实现
堆需要进行向下调整和向上调整建堆。先分别实现向下调整建堆和向上调整建堆。
2.1.1 向下调整建堆:
我们需要从最后一个父结点开始,进行向下调整建堆。直到根节点。
adjustdown(int parent)
{
int child = parent*2+1;//左孩子
while(child<container.size())
{
if(child+1<container.size()&&container.[child]<container.[child+1])
{
child++;
}
else if(container.[parent]<container.[child])
{
swap(container.[parent],container.[child]);
parent = child;
child = parent*2+1;
}
else break;
}
}
2.1.2 向上调整建堆
我们需要从最后一个子节点开始建堆。
adjustup(int child)
{
int parent = (child-1)/2;
while(parent>0)
{
if(container.[child]>container.[parent])
{
swap(container.[child],container.[parent]);
child = parent;
parent = (child-1)/2;
}
else break;
}
}
2.3 模拟实现
由于底层是堆结构,当删除第一个元素时,需要先将头尾置换,再将尾删除,再进行向下调整操作。
void pop()
{
swap(_con.[0], _com.[_con.size() - 1]);
_con.pop_back();
adjustdown(0);
}
当插入元素时,也是先插入到尾部,然后进行向上调整。
void push(const T& x)
{
_con.push_back(x);
adjustup(_con.size() - 1);
}