1.Stack介绍
stack是一种容器适配器,特点是后进先出,只能从容器的一端进行提取、插入、删除。stack的底层容器一般使用deque,除此之外list,vector也符合容器特性。
(1)deque
双端队列(Deque,全称为Double-Ended Queue)是一种具有队列和栈的性质的数据结构。它允许在队列的两端进行插入和删除操作,同时也具有先进先出(FIFO)和后进先出(LIFO)的特性。因此,双端队列是队列和栈的一种综合体。deque不仅能作为stack的底层容器,同时也能作为queue的底层容器。deque是一个非常实用的数据结构,它可以用来解决许多需要在队列头部和尾部进行频繁操作的问题。
(2)stack函数接口
由于stack底层有一个容器,因此stack的成员也就只包含一个容器,通过调用容器的函数来满足stack的函数需求。
stack需要以下函数接口:
#pragma once
using namespace std;
#include <deque>
namespace L
{
template <class T,class con = deque<T>>
class stack
{
public:
stack()
{
;
}
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
T& top()
{
return _con.back();
}
const T& top()const
{
return _con.back();
}
size_t size()const
{
return _con.size();
}
bool empty()const
{
return _con.empty();
}
private:
con _con;
};
}
2.Queue介绍
队列是一种容器适配器,特点是先进先出,从容器的一端插入元素,从另一端提取元素。其底层容器一般使用的是deque,如有特别要求,list也是能满足需求。
(1)queue函数接口
#pragma once
using namespace std;
#include <deque>
namespace L
{
template <class T, class con = deque<T>>
class queue
{
public:
queue()
{
;
}
void push(const T& x)
{
_c.push_back(x);
}
void pop()
{
_c.pop_front();
}
T& back()
{
return _c.back();
}
const T& back()const
{
return _c.back();
}
T& front()
{
return _c.front();
}
const T& front()const
{
return _c.front();
}
size_t size()const
{
return _c.size();
}
bool empty()const
{
return _c.empty();
}
private:
con _c;
};
}
3.优先级队列
优先级队列是一种容器适配器,特点是类似于堆,根据less排序,它的第一个元素总是最大元素,随时能进行插入元素,并且支持随机访问迭代器,而且内部需要始终保持堆结构。优先级队列的底层容器一般默认是vector,默认情况下优先级队列是大堆。
(1)函数接口
优先级队列也具有栈与队列相同的函数接口需求,只不过模拟实现的方式有点不同。
优先级队列的成员不仅具有容器,还具有仿函数成员。通过仿函数来确定是建大堆还是小堆。
构造函数:优先级队列提供默认的构造函数,还有通过迭代器区间来构造的函数。在通过迭代器区间来构造完成后,就需要进行建堆操作,而建堆则需要实现向下调整算法。
priority_queue()
{
;
}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
c.push_back(*first);
++first;
}
for (int i = (c.size() - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(i);
}
}
void AdjustDown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < c.size())
{
if (child + 1 < c.size() && comp(c[child], c[child + 1]))
{
++child;
}
if (comp(c[parent], c[child]))
{
swap(c[parent], c[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
代码中comp是仿函数,默认是less算法,向下调整算法即谁小谁交换。
POP:在删除函数中,同样需要向下调整算法。思路和堆的删除思路一样,队首元素与队尾元素交换,然后直接调用尾删函数,接着使用向下调整算法来保持内部是堆的结构。
void pop()
{
assert(c.size() > 0);
swap(c[0], c[c.size() - 1]);
c.pop_back();
AdjustDown(0);
}
PUSH:在插入函数中,由于是在队尾插入元素,因此需要向上调整算法。
void AdjustUp(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (comp(c[parent],c[child]))
{
swap(c[parent], c[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void push(const T& x)
{
c.push_back(x);
AdjustUp(c.size() - 1);
}
(2)完整模拟实现代码
#pragma once
#include <vector>
#include <functional>
#include <assert.h>
using namespace std;
namespace L
{
template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
priority_queue()
{
;
}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
c.push_back(*first);
++first;
}
for (int i = (c.size() - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(i);
}
}
void AdjustUp(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (comp(c[parent],c[child]))
{
swap(c[parent], c[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void AdjustDown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < c.size())
{
if (child + 1 < c.size() && comp(c[child], c[child + 1]))
{
++child;
}
if (comp(c[parent], c[child]))
{
swap(c[parent], c[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
bool empty() const
{
return c.empty();
}
size_t size() const
{
return c.size();
}
const T& top() const
{
return c[0];
}
void push(const T& x)
{
c.push_back(x);
AdjustUp(c.size() - 1);
}
void pop()
{
assert(c.size() > 0);
swap(c[0], c[c.size() - 1]);
c.pop_back();
AdjustDown(0);
}
private:
Container c;
Compare comp;
};
}