目录
1.认识栈和队列
栈是只在队尾进行插入和删除的线性表
队列是允许在一端进行插入操作而在另一端进行删除的线性表
都是线性表,那么有两种实现方式,即:顺序表,链表
栈 | 队列 | |
顺序表<vector> | 顺序栈 | 顺序队列 |
链表<list> | 链栈 | 链队列 |
因为可以有两种不同的底层存储方式,所以栈和队列被设计为容器适配器。
2.容器适配器
了解容器适配器之前,我们先来了解一下适配器:
适配器是一种设计模式,该模式可以让一种接口转换为用户所希望的另外一个接口
比如下图:用户希望得到一个交流电插头,就将标准插头和交流电适配器结合,就可以得到用户希望的交流电插头。
容器适配器是对已有的容器进行封装来实现其他容器。比如stack适配器对vector进行封装,提供接口,使其实现stack的功能。
容器适配器可以让开发者使用一种容器接口,但实际上使用的是另一种容器。
C++标准库提供了三种常见的容器适配器:栈(stack),队列(queue)和优先队列(priority——queue)
3.栈的实现
stack - C++ Referencehttps://legacy.cplusplus.com/reference/stack/stack/
代码实现:
template<class T, class container = deque<T>>
class stack
{
public:
void push(const T& x)
{
return Con.push_back(x);
}
void pop()
{
return 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:
container Con;
};
4.队列的实现
queue - C++ Referencehttps://legacy.cplusplus.com/reference/queue/queue/
代码实现:
template<class T, class container = deque<T>>
class queue
{
public:
void push(const T& x)
{
return Con.push_back(x);
}
void pop()
{
return Con.pop_front();
}
T& front()
{
return Con.front();
}
const T& front()const
{
return Con.front();
}
size_t size()const
{
return Con.size();
}
bool empty()const
{
return Con.empty();
}
private:
container Con;
};
5.deque容器
观察上述发现,我们给的容器默认参数都是deque,也侧面说明deque可以同时支持list和vector两种容器的操作
deque - C++ Referencehttps://legacy.cplusplus.com/reference/deque/deque/
deque的结构
deque是一个双开口的连续空间的数据结构。
双开口:可以在两端进行插入和删除操作。
5.1 deque的存储结构
但deque并不是一个连续空间,而是由一个一个小的连续空间拼接而成的。
中控数组:存放每一块连续空间的起始位置。当中控数组满的时候,扩容即可。
operater[](size_t i)
{
1.先看在不在第一个buff数组,如果在就找位置访问
2.如果不在第一个数组
在第几个buff=i/buffsize
在第几个buff的第几个=i%buffsize
}
5.2 deque的优缺点
相比vector:
极大的解决了头插和头删的问题
但[ ]不够极致,需要计算在哪个buff,在哪个buff 的第几个
相比list:
1.可以支持下标随机访问
2.cpu高速缓存效率不错
3.头尾插入和删除都不从,但是从中间删除很拉胯
5.3 deque的迭代器:
6.优先队列
优先级队列会对队内元素自动进行从大到小的排列。大的元素先出
优先级队列也是容器适配器,底层的数据结构是一个堆(默认是大堆),默认适配的容器是vector。
6.1仿函数的定义
仿函数是一种具有函数行为的类,在类内重载了运算符(),使对象可以像函数一样调用
实例代码
class Adder {
public:
int operator()(int a, int b) {
return a + b;
}
};
int main() {
Adder adder; // 创建一个仿函数对象
int result = adder(3, 4); // 调用仿函数
std::cout << "The result is: " << result << std::endl;
return 0;
}
6.2优先队列中使用仿函数
class Less
{
public:
bool operator()(const T& x,const T& y)
{
return x < y;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
6.3优先队列的实现
template<class T,class container=vector<T>,class compare=Less<T>>
class priority_queue
{
private:
container _con;
compare Com;
void AdjustUp(int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (Com(_con[child], _con[parent]))
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
void AdjustDown(int parent)
{
int child = parent * 2 + 1;
size_t n = _con.size();
while (child < n)
{
if (child + 1 < _con.size() && Com(_con[child], _con[child + 1]))
{
++child;
}
if (Com(_con[child], _con[parent]))
{
swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
public:
priority_queue()
{}
//typedef container::iterator InputIterator;
template<class InputIterator>
priority_queue(InputIterator first,InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
}
void push(const T& x)
{
_con.push_back(x);
AdjustUp(_con.size()-1);
}
void pop()
{
size_t n = _con.size();
swap(_con[0], _con[n - 1]);
_con.pop_back();
AdjustDown(0);
}
bool empty()
{
return _con.empty();
}
size_t size()const
{
return _con.size();
}
const T& top()
{
return _con[0];
}
};
测试案例:
void test_priority_queue2()
{
struct LessPDate
{
bool operator()(const Date* p1, const Date* p2)
{
return *p1 < *p2;
}
};
//新构造一个仿函数LessPDate,Less和Greater比较的都是其存储地址,每一次的结果都会变
mystl::priority_queue<Date*,vector<Date*>,LessPDate> pq;
pq.push(new Date(2023, 7, 20));
pq.push(new Date(2023, 6, 20));
pq.push(new Date(2023, 8, 20));
while (!pq.empty())
{
cout << *pq.top() << " ";
pq.pop();
}
cout << endl;
}
输出结果:2023-6-20 2023-7-20 2023-8-20