栈和队列的模拟实现

打印 上一主题 下一主题

主题 1023|帖子 1023|积分 3069

一. 回顾栈和队列

回顾一下栈和队列


  • 栈:stack:后进先出

    _ 队列:queue:先辈先出

    计划模式有很多种,鉴戒计划模式的参考书:计划模式 - 可复用的面向对象软件元素,得知统共有 23 种计划模式。
计划模式是是先辈们对代码开辟履历的总结,是解决特定题目的一系列套路,比如适配器模式,迭代器模式
栈和队列是容器适配器,而不是容器。


  • 迭代器模式:将迭代器封装之后提供统一的访问方式,优点:不暴露底层细节
  • 适配器模式:适配器其实是一种转换,通过已有的东西封装转换出你想要的东西。比如电源适配器(电压电流的转换,家里的电源电压通常是220v,而某个电子产品之许哟啊20v的电压,这个时候就必要用到适配器)
stack的利用
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include<stdio.h>
  3. #include<iostream>
  4. #include<stack>
  5. #include<queue>
  6. int main()
  7. {
  8.         std::stack<int> st1;
  9.         st1.push(1);
  10.         st1.push(2);
  11.         st1.push(3);
  12.         st1.push(4);
  13.         std::cout <<"在未删除之前st1的元素个数" << st1.size() << std::endl;
  14.         //栈是后进先出,所以现在想打印的话,打印的是栈顶元素
  15.         while (!st1.empty())
  16.         {
  17.                 std::cout << st1.top() << " ";
  18.                 //只能将栈顶元素删除,才能打印下一个元素
  19.                 st1.pop();
  20.         }
  21.         std::cout << std::endl;
  22.         std::cout << "在删除之后st1的元素个数" << st1.size() << std::endl;
  23.         std::stack<int> st2;
  24.         st2.push(8);
  25.         st2.push(7);
  26.         st2.push(6);
  27.         st2.push(5);
  28.         st2.swap(st1);
  29.         std::cout << "st1和st2交换之后,st1的元素" << std::endl;
  30.         while (!st1.empty())
  31.         {
  32.                 std::cout << st1.top() << " ";
  33.                 //只能将栈顶元素删除,才能打印下一个元素
  34.                 st1.pop();
  35.         }
  36.         std::cout << std::endl;
  37.         return 0;
  38. }
复制代码

二. stack的模拟实现

stack.h

先在私有部分将类的成员变量写出来,我们必要一个容器。
  1. //Container有可能是vector,也有可能是list
  2. Container _con;
复制代码
起首要知道stack栈都有什么接口,我们再实现

必要实现判空,返回栈中元素的个数,栈顶元素,尾插和尾删,交换。
用C++的好处就是可以直接利用容器的接口,我们现在想用vector或list来实现stack(在过程中,可以利用vector和list的接口。
从接口可以看出,无论容器是vector还是list,都可以利用尾插尾删,以及back(获取末了一个元素),empty(判空)。



  • 以是,模拟实现stack的时候,容器vector和list都可以利用
  • 注意点:利用利用vector,要么展开命名空间using namespace std,要么写std::vector
  1. #pragma once
  2. #include<stdio.h>
  3. #include<iostream>
  4. #include<vector>
  5. #include<list>
  6. namespace hou
  7. {
  8.         template<class T,class Container>
  9.         class stack
  10.         {
  11.         public:
  12.                 //向容器里插入数据(这里是实现stack,是尾插尾删)
  13.                 void push(const T& x)
  14.                 {
  15.                         _con.push_back(x);
  16.                 }
  17.                 void pop()
  18.                 {
  19.                         _con.pop_back();
  20.                 }
  21.                 const T& top()
  22.                 {
  23.                         return _con.back();
  24.                 }
  25.                 bool empty()
  26.                 {
  27.                         return _con.empty();
  28.                 }
  29.                 size_t size()
  30.                 {
  31.                         return _con.size();
  32.                 }
  33.         private:
  34.                 Container _con;
  35.         };
  36. }
复制代码
stack.cpp

  1. #include"stack_queue.h"
  2. int main()
  3. {
  4.         hou::stack<int,std::vector<int>> mystack1;
  5.         mystack1.push(11);
  6.         mystack1.push(12);
  7.         mystack1.pop();
  8.         std::cout<<mystack1.size()<<std::endl;
  9.         return 0;
  10. }
复制代码
三. queue的模拟实现

队列queue是先辈先出,也就是尾插头删

queue.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<iostream>
  4. #include<vector>
  5. #include<list>
  6. namespace hou
  7. {
  8.         template<class T,class Container>
  9.         class queue   
  10.         {
  11.         public:
  12.                 void push(const T& x)
  13.                 {
  14.                         _con.push_back(x);
  15.                 }
  16.                 void pop()
  17.                 {
  18.                         _con.pop_front();
  19.                 }
  20.                 const T& front()
  21.                 {
  22.                         return _con.front();
  23.                 }
  24.                 const T& back()
  25.                 {
  26.                         return _con.back();
  27.                 }
  28.                 bool empty()
  29.                 {
  30.                         return _con.empty();
  31.                 }
  32.                 size_t size()
  33.                 {
  34.                         return _con.size();
  35.                 }
  36.         private:
  37.                 Container _con;
  38.         };
  39. }
复制代码
test.cpp

  1. int main()
  2. {
  3.         hou::queue<int, std::list<int>> myqueue1;
  4.         myqueue1.push(1);
  5.         myqueue1.push(2);
  6.         myqueue1.push(3);
  7.         while (!myqueue1.empty())
  8.         {
  9.                 std::cout << myqueue1.front() << std::endl;
  10.                 myqueue1.pop();
  11.         }
  12.         return 0;
  13. }
复制代码
四. 了解dequeue

查看stack和queue的文档时发现:Container的缺省值不是vector或list,而是deque,deque是一个双端队列(两端都可以插入删除)
  1. queue:template <class T, class Container = deque<T> > class queue;
  2. list:template <class T, class Container = deque<T> > class stack;
复制代码
vector和list都有各自的缺陷

vector的空间是连续的,而list是一个一个的结点组成的(空间不是连续的)


  • vector的物理地址是连续的,可以利用[]访问元素(vector支持随机访问)。但同时也就导致头插头删非常麻烦,头插有扩容斲丧(先查看空间是否足够,再将背面的内容一个一个今后移),头删/中部删的效率低(背面的内容往前移)。

2. list的物理空间是不连续的,它无法通过[]来访问元素(不支持随机访问)。同时也致使CPU高速缓存掷中率低。但是list头插头删很容易,改变链接即可。
deque

deque兼具vector和list之长(既可以随机访问,又可以不那么)

底层实现:开一个又一个小的数组buffer,再用一个中控数组将这些buffer管理起来(中控数组它的每个元素是指针,是每个buffer的地址),以是每个buffer不用想着下一个buffer是谁,这个由中控数组在管理。
deque并不是真正连续的空间,而是由一段一段的(名字为buffer)的小空间拼接而成,再由中控数组来控制。



  • 如果中控数组满了呢?那就必要异地扩容,但也不复杂,将地址复制过来就OK。
  • 在deque中,不是将第一个buffer放在最前面,而是将中控数组的前几个位置空出来,为了之后必要头插做准备,如果前面有位置的话,就不用挪动数据了。
deque固然有queue和list的优点,但也有不敷的地方,无法取代它俩。

  • deque在随机访问的时候,必要知道某个数据在哪个buffer里?又是在这个buffer的第几个元素?(第几个buffer:用数据地点的位置/10。buffer的第几个元素:用数据地点的位置%10)。下标的随机访问有一定的斲丧,没有vector的随机访问快。
  • 若是想在某个buffer里插入数据或删除buffer里的某个数据,怎么做呢?
    (1)挪动这个数据背面的数据--------->效率低
    (2)只挪动当前buffer的数据----------->效率还可以,(删除某个buffer的数据且只挪动当前buffer的元素)但是会导致每个buffer的个数不一样,计算在第几个buffer的第几个元素会更麻烦。以是deque在中间插入删除也有一定的斲丧,相比list中间插入删除不敷机动,没有list快。栈和队列一般是插入或删除头尾的数据,利用deque当默认适配容器会比力适合!!!
deque不适合遍历。由于在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下。而序列式场景中,可能必要经常遍历,因此在实际中,必要线性结构时,大多数情况下优先思量vector和list,deque的应用并不多,而现在能看到的一个应用就是,STL用其作为stack和queue**的底层数据结构
deque作为stack和queue的底层默认容器 :在stack中元素增长时,deque比vector的效率高(扩容时不必要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存利用率高

总结

模拟实现stack:vector和list都可以利用
模拟实现queue:只能利用容器list(队列是先辈先出,也就是头删,而vector没有头删这个接口)
实现stack和queue的默认适配容器是deque。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南飓风

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表