大家好,我是bug!今天我们来讲讲容器适配器:
(代码可能会有一点问题,请各位老铁指正 )
容器适配器:简单可以理解为通过对不适用的容器进行封装处理,将不适用的序列式容器(包括 vector、deque 和 list)变得适用。
可以这样认为,容器配置器不会要求底层容器的种类,只要求底层的容器可以支持适配器的功能。(不同的容器效率也不一样)
栈: 又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
常用接口:
函数 | 用法 |
---|---|
stack() | 构造空的栈 |
empty() | 检测stack是否为空 |
size() | 返回stack中元素的个数 |
top() | 返回栈顶元素的引用 |
push() | 将元素val压入stack中 |
pop() | 将stack中尾部的元素弹出 |
模拟实现⬇️ ⬇️:
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::deque;
namespace lz
{
template<class T,class Container = deque<T>>
class stack
{
private:
Container _con;
public:
stack()
:_con()
{}
//入栈,出栈
void push(const T& val) { _con.push_back(val); }
void pop() { if (empty()) return; _con.pop_back(); }
//元素个数,判空
size_t size() { return _con.size(); }
bool empty() { return size() == 0; }
//栈顶数据(可修改和不可修改)
T& top(){ return _con.back();}
const T& top()const { return _con.back(); }
};
}
#include"stack.h"
void Test_stack()
{
//默认容器:双端队列
lz::stack<int> s1;
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
while (!s1.empty())
{
cout << s1.top() << endl;
s1.pop();
}
cout << s1.size() << endl;
}
int main()
{
Test_stack();
return 0;
}
队列:一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
常用接口:
函数 | 用法 |
---|---|
queue() | 构造空的队列 |
empty() | 检测队列是否为空,是返回true,否则返回false |
size() | 返回队列中有效元素的个数 |
front() | 返回队头元素的引用 |
back() | 返回队尾元素的引用 |
push() | 在队尾将元素val入队列 |
pop() | 将队头元素出队列 |
模拟实现⬇️ ⬇️:
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::deque;
namespace lz
{
template<class T, class Container = deque<T>>
class queue
{
private:
Container _con;
public:
queue()
:_con()
{}
//插入删除
void push(const T& val) { _con.push_back(val); }
void pop() {if (empty())return; _con.pop_front();}
//数据元素,判空
size_t size() { return _con.size(); }
bool empty() { return _con.size() == 0; }
//获得队头和队尾元素
T& front() { return _con.front(); }
const T& front()const { return _con.front(); }
//T& back() { return _con.back; }
//const T& back()const { return _con.back; }
};
}
#include"queue.h"
void Test_queue()
{
lz::queue<int> q1;
q1.push(1);
q1.push(2);
q1.push(3);
q1.push(4);
while (!q1.empty())
{
cout << q1.front() << endl;
q1.pop();
}
cout << q1.size() << endl;
}
int main()
{
//Test_stack();
Test_queue();
return 0;
}
注意❗️ ❗️
对于栈和队列,我们采用的默认容器都是双端队列。(双端队列支持头尾的插入和删除)
stack和queue都不提供迭代器,因为stack和queue元素的进出都要符合各自的要求,迭代器支持修改遍历功能,所以不提供。
(下面就是今天的重头戏了)
如果我们给每个元素都分配一个数字来标记其优先级,不妨设较小的数字具有较高的优先级,这样我们就可以在一个集合中访问优先级最高的元素并对其进行查找和删除操作了。这样,我们就引入了优先级队列这种数据结构。
优先级队列(priority queue): 是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有(1)查找(2)插入一个新元素 (3)删除 一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 。对于优先权相同的元素,可按先进先出次序处理或按任意优先权进行。
注意❗️ ❗️
优先级队列的底层是堆(默认建的是大堆),每次插入删除数据都会进行调整。
理论上来说,优先级队列不能够算作队列,不符合队列先进先出的原则。(包括后面我们要讲的双端队列)
常用接口:
constructor(构造函数) | 用法 |
---|---|
priority_queue() | 空的优先级队列 |
priority_queue(Iterator first, Iterator last) | 迭代器区间进行构造 |
函数 | 用法 |
---|---|
empty | 判断优先级队列是否为空 |
size | 计算队列中元素个数 |
top | 取队列中优先级最高的元素(队头元素) |
push | 从队尾插入元素 |
pop | 删除优先级最高的元素(队头的元素) |
这里我们来实现一个简易的priority_queue⬇️ ⬇️:
#include
#include
using std::cin;
using std::cout;
using std::endl;
namespace lz
{
//仿函数
template<class T>
struct less
{
bool operator()(const T& left, const T& right)
{
return left < right;
}
};
template<class T>
struct greater
{
bool operator()(const T& left, const T& right)
{
return left > right;
}
};
//优先队列
template
<class T,class Container = std::vector<T>,class Compare = less<T>>
class priority_queue
{
public:
Compare Com;
priority_queue()
:_con()
{}
template<class Iterator>
priority_queue(Iterator first, Iterator last)
:_con(first, last)
{
int parent = (size() - 2) >> 1;
for(int i = parent; i >= 0 ; i--)
adjust_down(i);
}
//队尾入队
void push(const T& val) { _con.push_back(val); adjust_up(size() - 1); }
//队头出队
void pop()
{
if (!empty())
{
std::swap(_con.front(), _con.back());
_con.pop_back();
adjust_down(0);
}
//因为部分的容器不提供头删,所以我们通过交换后进行删除
//不建议使用erase
}
//容量
size_t size()const { return _con.size(); }
//返回队头元素
const T& top()const { return _con.front(); }
//判空
bool empty()const { return _con.empty(); }
private:
//向下调整法
void adjust_down(int parent)
{
int child = parent * 2 + 1;
while (child < size())
{
if (child + 1 < size() && Com(_con[child] , _con[child + 1]))
{
child++;
}
if (Com(_con[parent] , _con[child]))
{
std::swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//向上调整法
void adjust_up(int child)
{
int parent = (child - 1) >> 1;
while (child)
{
if (Com(_con[parent] , _con[child]))
{
std::swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
{
break;
}
}
}
Container _con;
};
}
#include"priority_queue.h"
void Test1()
{
lz::priority_queue<int, std::vector<int>, lz::less<int>> pq1;
pq1.push(1);
pq1.push(2);
pq1.push(3);
pq1.push(4);
pq1.push(5);
pq1.push(6);
while (!pq1.empty())
{
cout << pq1.top() << " ";
pq1.pop();
}
cout << endl;
std::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(9);
v1.push_back(8);
lz::priority_queue<int, std::vector<int>, lz::greater<int>>
pq(v1.begin(), v1.end());
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
}
int main()
{
Test1();
return 0;
}
注意❗️ ❗️
在优先级队列的实现中,我们使用了仿函数(以后细讲),通过less和greater来控制队列元素的优先级,默认给less(less:数据大的优先级高,降序排列;greater:数据小的优先级高,按升序排列)。