stack简介以及使用
queue简介以及使用
priority_queue简介以及使用
仿函数介绍
什么是适配器模式
stack的模拟实现
queue的模拟实现
priority_queue的模拟实现
外强中干的deque
stack的简介以及使用
stack就是我们先前在数据结构里面使用过的栈这个数据结构,它仅仅允许在一段插入和删除数据,这一段被称作为栈顶。 同样,我们也通过官方文档来认识一下stack有什么接口可以使用:
接下来我们就通过一段简单的代码来看一看怎么使用stack
#include#include using namespace std; void test() { std::stack st; st.push(1); st.push(2); st.push(3); st.push(4); while (!st.empty()) { int top = st.top(); cout << top << " "; st.pop(); } cout << endl; } int main() { test(); return 0; }
关于stack的功能介绍就到这里,以后需要使用数据结构里面的stack我们就无需自己写了。
queue的简介和使用
queue是我们数据结构里面的队列,遵循先进先出的原则。 我们同样是通过官方文档来看一看queue的使用。
和stack不一样的是,queu可以取队头的数据的同时也可以取队尾的数据,但是我们都是取队头的数据。
#include#include using namespace std; void test() { queue q; q.push(1); q.push(2); q.push(3); q.push(4); while (!q.empty()) { int font = q.front(); cout << font << " "; q.pop(); } cout << endl; } int main() { test(); return 0; }
同样对于queue的使用我们就介绍到这里。
priority_queue的使用和介绍
实际上,除了普通的栈和队列以外。官方库还提供了优先级队列,优先级队列的行为就是数据结构里面的堆。下面我们来看一看优先级队列priority_queue怎么使用。一样,我们还是通过官方文档来看。
接下来我们也透过代码来看一看priority_queue怎么使用
#include#include //priority_queue也在这个文件 using namespace std; void test() { priority_queue pq; pq.push(4); pq.push(6); pq.push(2); pq.push(8); while (!pq.empty()) { int top = pq.top(); cout << top << " "; pq.pop(); } cout << endl; } int main() { test(); return 0; }
这里默认形成的是大堆,而如果要生成一个小堆,那么就要传递仿函数。
#include#include #include //官方库定义的仿函数所在的头文件 void test() { //这里的greator是一个仿函数 priority_queue ,greater >pq; pq.push(4); pq.push(6); pq.push(2); pq.push(8); while (!pq.empty()) { int top = pq.top(); cout << top << " "; pq.pop(); } cout << endl; } int main() { test(); return 0; }
关于priority_queue的使用就介绍到这里,下面和我一起认识一下仿函数。
仿函数介绍
我们介绍一下什么是仿函数。首先我们回顾C语言的qsort,我们知道qsort用了第四个参数是函数指针。因为函数指针的存在,所以qsort可以根据函数指针指定的排序规则排序,但是函数指针的结构太复杂了!而且不符合面向对象程序的设计! 为了能够做到指定规则排序,所以C++设计了仿函数来代替函数指针的作用,而仿函数的本质就是一个operator()的类。
//设计一个仿函数。 //也可以用class,不过operator()就要声明成public的函数 templatestruct Less { //重载() bool operator()(const T& a,const T& b)const { return a 官方库里面写好了less和greater两个仿函数,它们定义在functional头文件里面。
有的时候,我们需要自己定义仿函数,不过大多数场景下,官方库里面定义的这两个仿函数已经就够用了。
什么是适配器模式
仔细观察官方库里面的stack和queue的声明template> class stack; template > class queue; 我们看到,这里给了一个模板参数Container 其实严格意义上来说,stack和queue并不是容器,而是容器适配器。 既然是容器适配器,就要提到很重要的设计思想---->适配器模式。
所谓的适配器模式,就是借助类似"充电头"这样的转换器来帮助转换出我们想要的,而这里的充电头就是模板参数Container!以stack为例,如果这里的Container是vector,那么我们的栈就是数组栈,而如果是list,那么生成的就是链式栈! 借助适配器,我们无需关心stack使用什么数据结构实现。同理queue也是一样!
stack的模拟实现
接下来我们来模拟实现一下stack#pragma once #include#include #include #include using namespace std; namespace chy { //stack是容器适配器,不独立实现,而是借用其他容器生成 template
> class stack { public: 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(); } bool empty() const { return _con.empty(); } size_t size() const { return _con.size(); } private: Container _con; }; } void test_st() { chy::stack st; st.push(1); st.push(2); st.push(3); st.push(4); cout << "namespace chy" << endl; while (!st.empty()) { int top = st.top(); cout << top << " "; st.pop(); } cout << endl; } int main() { test_st(); return 0; } 这里的默认使用的是deque进行适配,deque是一个可以在头尾快速插入删除的双端队列,并且能够支持随机访问,但是其实是一个"外强中干"的数据结构,最后我们会介绍一下这个容器。
queue的模拟实现
接下来我们来模拟实现以下queue#pragma once #include#include #include
using namespace std; namespace chy { template > class queue { //使用其他容器适配生成,不可以使用vector适配 //因为vector没有pop_front()成员 public: void push(const T& val) { _con.push_back(val); } void pop() { _con.pop_front(); } bool empty() const { return _con.empty(); } T& front() { return _con.front(); } T& back() { return _con.back(); } const T& front()const { return _con.front(); } const T& back() const { return _con.back(); } private: Container _con; }; } void test_queue1() { //使用list适配,不能用vector,因为vector没有pop_front() chy::queue >q; q.push(1); q.push(2); q.push(3); q.push(4); cout << "namespace chy" << endl; while (!q.empty()) { int top = q.front(); cout << top << " "; q.pop(); } cout << endl; } int main() { test_queue1(); return 0; }
priority_queue的模拟实现
最后就是优先级队列的模拟,相比于前面两个直接适配。优先级队列push要向上调整,而pop()的时候要向下调整,所以相对会复杂一点。#pragma once #include#include #include #include using namespace std; namespace chy { //compare是一个仿函数 template ,class Compare=less > class priority_queue { public: void push(const T& val) { _con.push_back(val); _adjustup(_con.size() - 1); } void pop() { //删除交换堆顶,然后删除 swap(_con[0], _con[_con.size() - 1]); _con.pop_back(); _adjustdown(0); } const T& top()const { return _con[0]; } bool empty()const { return _con.empty(); } size_t size()const { return _con.size(); } //堆需要调整,所以我们要提供调整算法 private: void _adjustup(size_t child) { Compare func; size_t parent = (child - 1) / 2; while (child > 0) { if (func(_con[parent],_con[child])) { swap(_con[parent], _con[child]); child = parent; parent = (child - 1) / 2; } //其他位置都处理好了 else { break; } } } void _adjustdown(size_t parent) { Compare func; size_t child = 2 * parent + 1; while (child < _con.size()) { if (child + 1 < _con.size() && func(_con[child],_con[child+1])) { ++child; } if (func(_con[parent], _con[child])) { swap(_con[parent], _con[child]); parent = child; child = 2 * parent + 1; } else { break; } } } private: Container _con; }; } void test_pq() { chy::priority_queue pq; pq.push(4); pq.push(6); pq.push(2); pq.push(8); cout << "namespace chy" << endl; while (!pq.empty()) { int top = pq.top(); cout << top << " "; pq.pop(); } cout << endl; } void test_pq1() { chy::priority_queue , greater > pq; pq.push(4); pq.push(6); pq.push(2); pq.push(8); cout << "namespace chy" << endl; while (!pq.empty()) { int top = pq.top(); cout << top << " "; pq.pop(); } cout << endl; } int main() { test_pq(); test_pq1(); //test(); return 0; }
外强中干的deque
可能细心的你已经注意到了,stack和queue的默认适配容器都是deque,那么这个deque究竟是何许神仙呢?其实这个deque并没有什么特别神奇的地方,它的结构非常复杂,下面是一个deque的结构图
其实,deque本质上划分了一块一块很小的内存,接着使用了1个指针数组来记录每一个小内存块的地址(为了支持随机访问)。但其实真实随机访问的效率比vector慢非常多!所以看似有着vector和list的所有优点,但是实际上并不是特别实用。但是deque的优点特别适合stack和queue,所以stack和queue都是默认使用deque来适配
以上就是本篇文章的全部内容,如果有不足或错误之处还望指出。希望大家共同进步。



