启迪思维:循环顺序队列

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

一:概念

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

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

二:示例图

 启迪思维:循环顺序队列_第1张图片

三:栈的应用

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

2、舞伴问题

3、迷宫问题

四:代码分析

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

1、入队节点

入队列示例图如下:

启迪思维:循环顺序队列_第2张图片

 代码分析如下: 

 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、出队节点

出队列示例图如下:

启迪思维:循环顺序队列_第3张图片

 代码分析如下:

 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 }

运行结果如图:

启迪思维:循环顺序队列_第4张图片

:环境

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

2、开发工具:Eclipse+make

:题记

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

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

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

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

 

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

你可能感兴趣的:(队列)