前幾天和女朋友一起參加一個技術沙龍,走到地鐵又想到自己的疑問,為啥很大多數電梯只有向上的電梯,而沒有向下的;以前想過各種解釋(節約成本、基於安全考慮等等),女朋友說因為向上的電梯都離開地鐵,設計有電梯可以讓人快點離開,向下是進入地鐵,沒有電梯是讓人慢點進入地鐵,這樣的設計一定程度緩解地鐵人流量壓力。覺得是目前我想過和聽過最合理的解釋,也許有一天會有更合理的解釋,每一次的思考都會離真理更近,沒事多思考,突然有天想通一個困擾你很久的問題,真的很幸福。
一:概念
隊列,又稱為佇列(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_ */
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_ */
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);
歡迎繼續閱讀“啟迪思維:數據結構和算法”系列
