啟迪思維:循環順序隊列


  前幾天和女朋友一起參加一個技術沙龍,走到地鐵又想到自己的疑問,為啥很大多數電梯只有向上的電梯,而沒有向下的;以前想過各種解釋(節約成本、基於安全考慮等等),女朋友說因為向上的電梯都離開地鐵,設計有電梯可以讓人快點離開,向下是進入地鐵,沒有電梯是讓人慢點進入地鐵,這樣的設計一定程度緩解地鐵人流量壓力。覺得是目前我想過和聽過最合理的解釋,也許有一天會有更合理的解釋,每一次的思考都會離真理更近,沒事多思考,突然有天想通一個困擾你很久的問題,真的很幸福。

一:概念

  隊列,又稱為佇列(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)

 

歡迎繼續閱讀“啟迪思維:數據結構和算法”系列


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM