• 启迪思维:循环顺序队列


      前几天和女朋友一起参加一个技术沙龙,走到地铁又想到自己的疑问,为啥很大多数电梯只有向上的电梯,而没有向下的;以前想过各种解释(节约成本、基于安全考虑等等),女朋友说因为向上的电梯都离开地铁,设计有电梯可以让人快点离开,向下是进入地铁,没有电梯是让人慢点进入地铁,这样的设计一定程度缓解地铁人流量压力。觉得是目前我想过和听过最合理的解释,也许有一天会有更合理的解释,每一次的思考都会离真理更近,没事多思考,突然有天想通一个困扰你很久的问题,真的很幸福。

    一:概念

      队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。

      队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加,还在需要考试的鞋童有事没事看看概念(免得被考试鄙视了),已经在写代码的鞋童多想想为什么下面代码会这样写,或者想想有没有更好实现方式。

    二:示例图

     队列例图

    三:栈的应用

    1、操作系统底层消息实现

    2、舞伴问题

    3、迷宫问题

    四:代码分析

      顺序队列示例图所示,分为普通顺序存储和循环存储,普通顺序存储节点出队后,其所占空间无法再次利用,导致浪费空间,为充分利用存储空间,克服"假溢出"现象将存储空间想象为一个首尾相接的圆环。下面代码分析为循环存储

    1、入队节点

    入队列示例图如下:

    入队列

     代码分析如下: 

     1 /**
     2  *元素入队列
     3  */
     4 void EnQueue(const T &e){
     5 
     6     //判断队列是否已经满,如果慢,则自动增长空间为原来的2倍
     7     if(IsFull()){
     8         //创建一个新的指针数组
     9         T *newBase = new T[size*2];
    10         //复制原空间的数据到新创建空间
    11         memcpy(newBase,base,sizeof(base)+1);
    12         //这种删除内存很危险,更多请参考boost里shared_array
    13         delete[] base;
    14         //指向新空间
    15         base = newBase;
    16 
    17         //初始化队头下标
    18         front = 0;
    19         //初始化队尾下标
    20         rear = size -1;
    21         //更新队列大小
    22         size = size * 2;
    23     }
    24     //元素入队尾
    25     *(base + rear) = e;
    26     //改变队尾下标,看不懂下面表达式,可以用特例带入推理下,或者多几个图试试
    27     rear = ++rear % size;
    28 }

    2、出队节点

    出队列示例图如下:

    出对列

     代码分析如下:

     1 /**
     2  *元素出队列
     3  */
     4 bool DeQueue(T &e){
     5     //判断是否为空
     6     if(IsEmpty()){
     7         return false;
     8     }
     9     //取出队头元素
    10     e = *(base + front);
    11     //改变队头下标
    12     front = ++front % size;
    13 
    14     return true;
    15 }

    3、清空队列

    1 //清空队列
    2 void Clear(){
    3     //如果不等空,则初始化front,rear
    4     if(!IsEmpty()){
    5         //保证比较好缩进和空格,可以让代码跟美观和易阅读,慢慢就能玩好程序艺术
    6         front = rear = 0;
    7     }
    8 }

    4、判断是否为空

    1 /**
    2  *判断队列是否为空
    3  */
    4 bool IsEmpty() const {
    5     return front == rear;
    6 }

    5、判断队列是否满

     1 /**
     2  *判断队列是否已经满
     3  *要是春节火车站有这个判断该有多好,不知道效率会高多少倍
     4  */
     5 bool IsFull() const {
     6     //关于(rear + 1) % size == front这个表达式看不懂没有关系,
     7     //比如size = 5,front = 0,那么rear = 4,则队列满
     8     //比如size = 5,front = 1,那么rear = 0(这个地方什么等于1,参考EnQueue方法),则队列满
     9     //在很多看不懂算法,可以用带入特例,帮忙我们更好理解
    10     return (rear + 1) % size == front;
    11 }

    6、获取队头元素

     1 /**
     2  *获取队列头元素
     3  */
     4 bool GetFront(T &e) const {
     5     //如果队列为空,则不处理
     6     if(IsEmpty()){
     7         return false;
     8     }
     9 
    10     //直接获取队列头元素,如果对*(base + front);有疑惑请学习指针、数组、指针数组
    11     e = *(base + front);
    12     return true;
    13 }

    7、计算队列大小

    1 /**
    2  *计算队列大小
    3  */
    4 int GetSize() const {
    5     return rear - front;
    6 }

    8、完整代码

    ArrayQueue.h: 

      1 /*
      2  * ArrayQueue.h
      3  *
      4  *  Created on: May 16, 2013
      5  *      Author: sunysen
      6  */
      7 
      8 #ifndef ARRAYQUEUE_H_
      9 #define ARRAYQUEUE_H_
     10 
     11 /**
     12  *循环顺序队列实现代码
     13  */
     14 template <class T>
     15 class ArrayQueue{
     16 private:
     17     int size;//队列大小
     18     int front;//队列下标
     19     int rear;//队尾下标
     20     T *base;//元素存储空间
     21 public:
     22     /**
     23      * 构造函数和初始化列表
     24      * explicit  只对构造函数起作用,用来抑制隐式转换
     25      * 想要用C++写出高效的代码,需要我们了解编译器为我们写代码做哪些转换
     26      * 比如(对象如何初始化,销毁,为啥不能根据返回值内型不通做重载,
     27      * 一个cpp文件修改,包含这个cpp文件都需要重新编译等等)
     28      * 在这方面我也是菜鸟,想了解跟多,可以读下<<深度探索C++对象模型>>
     29      */
     30     explicit ArrayQueue(int k):size(k),front(0),rear(0),base(new T[k]){
     31     }
     32     /**
     33      *析构函数
     34      */
     35     ~ArrayQueue(){
     36         //这种删除内存很危险,更多请参考boost里shared_array
     37         //专家都建议,在实际开发中尽量少用数组,用vector代替
     38         delete[] base;
     39     }
     40     /**
     41      *判断队列是否为空
     42      */
     43     bool IsEmpty() const {
     44         return front == rear;
     45     }
     46 
     47     /**
     48      *计算队列大小
     49      */
     50     int GetSize() const {
     51         return rear - front;
     52     }
     53 
     54     /**
     55      *获取队列头元素
     56      */
     57     bool GetFront(T &e) const {
     58         //如果队列为空,则不处理
     59         if(IsEmpty()){
     60             return false;
     61         }
     62 
     63         //直接获取队列头元素,如果对*(base + front);有疑惑请学习指针、数组、指针数组
     64         e = *(base + front);
     65         return true;
     66     }
     67 
     68     /**
     69      *判断队列是否已经满
     70      *要是春节火车站有这个判断该有多好,不知道效率会高多少倍
     71      */
     72     bool IsFull() const {
     73         //关于(rear + 1) % size == front这个表达式看不懂没有关系,
     74         //比如size = 5,front = 0,那么real = 4,则队列满
     75         //比如size = 5,front = 1,那么real = 0(这个地方什么等于1,参考EnQueue方法),则队列满
     76         //在很多看不懂算法,可以用带入特例,帮忙我们更好理解
     77         return (rear + 1) % size == front;
     78     }
     79 
     80     //清空队列
     81     void Clear(){
     82         //如果不等空,则初始化front,rear
     83         if(!IsEmpty()){
     84             //保证比较好缩进和空格,可以让代码跟美观和易阅读,慢慢就能玩好程序艺术
     85             front = rear = 0;
     86         }
     87     }
     88 
     89     /**
     90      *元素入队列
     91      */
     92     void EnQueue(const T &e){
     93 
     94         //判断队列是否已经满,如果慢,则自动增长空间为原来的2倍
     95         if(IsFull()){
     96             //创建一个新的指针数组
     97             T *newBase = new T[size*2];
     98             //复制原空间的数据到新创建空间
     99             memcpy(newBase,base,sizeof(base)+1);
    100             //这种删除内存很危险,更多请参考boost里shared_array
    101             delete[] base;
    102             //指向新空间
    103             base = newBase;
    104 
    105             //初始化队头下标
    106             front = 0;
    107             //初始化队尾下标
    108             rear = size -1;
    109             //更新队列大小
    110             size = size * 2;
    111         }
    112         //元素入队尾
    113         *(base + rear) = e;
    114         //改变队尾下标,看不懂下面表达式,可以用特例带入推理下,或者多几个图试试
    115         rear = ++rear % size;
    116     }
    117     /**
    118      *元素出队列
    119      */
    120     bool DeQueue(T &e){
    121         //判断是否为空
    122         if(IsEmpty()){
    123             return false;
    124         }
    125         //取出队头元素
    126         e = *(base + front);
    127         //改变队头下标
    128         front = ++front % size;
    129 
    130         return true;
    131     }
    132     /**
    133      * 测试队列所有的方法
    134      */
    135     void test(){
    136         std::cout<<"-----------EnQueue queue begin------------"<<std::endl;
    137         for(size_t i = 1; i < 5; ++i){
    138             EnQueue(i);
    139         }
    140         std::cout<<"-----------EnQueue queue end------------"<<std::endl;
    141 
    142         std::cout<<"frist queue length="<<GetSize()<<std::endl;
    143 
    144         std::cout<<"-----------GetFront queue begin------------"<<std::endl;
    145         bool isFront = false;
    146         T ee;
    147         isFront = GetFront(ee);
    148         if(isFront){
    149             std::cout<<"ee is value = "<<ee<<"\n";
    150         }else{
    151             std::cout<<"ee is value = empty"<<"\n";
    152         }
    153         std::cout<<"-----------GetFront queue end------------"<<std::endl;
    154 
    155         std::cout<<"-----------DeQueue queue begin------------"<<std::endl;
    156 
    157         T e;
    158         for(size_t i = 1; i < 5; ++i){
    159             DeQueue(e)    ;
    160             std::cout<<"e is value = "<<e<<"\n";
    161         }
    162         std::cout<<"-----------DeQueue queue end------------"<<std::endl;
    163 
    164         std::cout<<"secend queue size="<<GetSize()<<std::endl;
    165         Clear();
    166 
    167         std::cout<<"-----------GetFront queue begin------------"<<std::endl;
    168         T eee;
    169         isFront = GetFront(eee);
    170         if(isFront){
    171             std::cout<<"eee is value = "<<eee<<"\n";
    172         }else{
    173             std::cout<<"eee is value = empty"<<"\n";
    174         }
    175         std::cout<<"-----------GetFront queue end------------"<<std::endl;
    176 
    177         std::cout<<"third queue size="<<GetSize()<<std::endl;
    178     }
    179 
    180 };
    181 
    182 #endif /* ARRAYQUEUE_H_ */
    View Code

     Common.h:

     1 /*
     2  * Common.h
     3  *
     4  *  Created on: May 17, 2012
     5  *      Author: sunysen
     6  */
     7 
     8 #ifndef COMMON_H_
     9 #define COMMON_H_
    10 
    11 #include <iostream>
    12 #include "memory"
    13 #include "string"
    14 #include "string.h"
    15 #include <math.h>
    16 #include "core/node/LNode.h"
    17 
    18 
    19 using namespace std;
    20 #endif /* COMMON_H_ */
    View Code

    9、运行结果

     测试队列完整代码:

     1 /**
     2  * 测试队列所有的方法
     3  */
     4 void test(){
     5     std::cout<<"-----------EnQueue queue begin------------"<<std::endl;
     6     for(size_t i = 1; i < 5; ++i){
     7         EnQueue(i);
     8     }
     9     std::cout<<"-----------EnQueue queue end------------"<<std::endl;
    10 
    11     std::cout<<"frist queue length="<<GetSize()<<std::endl;
    12 
    13     std::cout<<"-----------GetFront queue begin------------"<<std::endl;
    14     bool isFront = false;
    15     T ee;
    16     isFront = GetFront(ee);
    17     if(isFront){
    18         std::cout<<"ee is value = "<<ee<<"\n";
    19     }else{
    20         std::cout<<"ee is value = empty"<<"\n";
    21     }
    22     std::cout<<"-----------GetFront queue end------------"<<std::endl;
    23 
    24     std::cout<<"-----------DeQueue queue begin------------"<<std::endl;
    25 
    26     T e;
    27     for(size_t i = 1; i < 5; ++i){
    28         DeQueue(e)    ;
    29         std::cout<<"e is value = "<<e<<"\n";
    30     }
    31     std::cout<<"-----------DeQueue queue end------------"<<std::endl;
    32 
    33     std::cout<<"secend queue size="<<GetSize()<<std::endl;
    34     Clear();
    35 
    36     std::cout<<"-----------GetFront queue begin------------"<<std::endl;
    37     T eee;
    38     isFront = GetFront(eee);
    39     if(isFront){
    40         std::cout<<"eee is value = "<<eee<<"\n";
    41     }else{
    42         std::cout<<"eee is value = empty"<<"\n";
    43     }
    44     std::cout<<"-----------GetFront queue end------------"<<std::endl;
    45 
    46     std::cout<<"third queue size="<<GetSize()<<std::endl;
    47 }

    运行结果如图:

    队列测试图

    :环境

    1、运行环境:Ubuntu 10.04 LTS+VMware8.0.4+gcc4.4.3

    2、开发工具:Eclipse+make

    :题记

    1、上面的代码难免有bug,如果你发现代码写的有问题,请你帮忙指出,让我们一起进步,让代码变的更漂亮和更健壮;

    2、我自己能手动写上面代码,离不开郝斌、高一凡、侯捷、严蔚敏等老师的书籍和视频指导,在这里感谢他们;

    3、鼓励自己能坚持把更多数据结构方面的知识写出来,让自己掌握更深刻,也顺便冒充下"小牛"

    4、运行上面的代码还需要在包含一个公共的头文件(Common.h)

     

    欢迎继续阅读“启迪思维:数据结构和算法”系列

    一根球杆,几行代码,品世间酸甜苦辣

    如果你喜欢这篇文章,欢迎推荐

    年轻人有理想、有野心,更需要脚踏实地做事情

  • 相关阅读:
    jQuery选择器
    js创建对象的几种方式
    call()和apply()
    js实现二分查找
    关于HTTP请求
    node/npm命令收集
    微信H5开发笔记——记录开发中用到的知识(手机摇动、事件捕获、wechat禁止分享、正则、promise...)
    css额外学习笔记
    gulp 结构和运行步骤——笔记1
    手机版H5开发--收集大神们的笔记
  • 原文地址:https://www.cnblogs.com/sunysen/p/3110487.html
Copyright © 2020-2023  润新知