中午在食堂打飯,真是一個使人頭疼的事情,去食堂的路上也老是步伐匆匆,爲何啊,這還用說,遲一點去,你就會知道什麼叫作人山人海了,在食堂排隊的時候,相比較學生來講,打飯阿姨畢竟是少數,在每一個窗口都有人的時候,難免咱們就得等待,直到前面的一個學生打完飯離開,後面排隊的人才能夠繼續向前走,直到輪到本身,別提多費勁了,可是秩序和規則倒是咱們每一個人都應該遵照的,也只能抱怨本身來的遲了前端
這種 「先進先出」 的例子就是咱們所講的基本數據結構之一 」隊列「node
例子補充:用電腦的時候,有時候機器會處於疑似死機的狀態, 鼠標點什麼彷佛都沒有用,雙擊任何快捷方式都不動,就當你失去耐心,打算reset的時候,忽然它就像酒醒了同樣,把你剛纔點擊的全部操做所有按照順序執行了一遍,這實際上是由於操做系統中的多個程序隱須要經過一個通道輸出,而按照前後次序排隊等待形成的 ——《大話數據結構》ios
定義:隊列是一種只容許在一段進行刪除操做,在另外一端進行插入操做的線性表數組
容許插入的一段稱做隊尾 (rear),容許刪除的的一端稱爲隊頭 (front)微信
隊列的數據元素又叫作隊列元素,在隊列中插入一個隊列元素稱爲入隊,從隊列中刪除一個隊列元素稱爲出隊 ,也正是由於隊列只容許在一段插入,另外一端刪除,因此這也就是咱們前面例子中體現出來的先進先出 (FIFO-first in first out) 的概念數據結構
補充:除此以外,還有的隊列叫作雙端隊列,也就是能夠在表的兩邊進行插入和刪除操做的線性表函數
雙端隊列分類:性能
輸出受限的雙端隊列:刪除操做限制在表的一段進行,而插入操做容許早表的兩端進行spa
插入操做限制在表的一段進行,而刪除操做容許在表的兩端進行操作系統
#ifndef _QUEUE_H_ #define _QUEUE_H_ #include <exception> using namespace std; // 用於檢查範圍的有效性 class outOfRange:public exception { public: const char* what()const throw() { return "ERROR! OUT OF RANGE.\n"; } }; // 用於檢查長度的有效性 class badSize:public exception { public: const char* what()const throw() { return "ERROR! BAD SIZE.\n"; } }; template <class T> class Queue { public: //判隊空 virtual bool empty() const = 0; //清空隊列 virtual void clear() = 0; //求隊列長度 virtual int size() const = 0; //入隊 virtual void enQueue(const T &x) = 0; //出隊 virtual T deQueue() = 0; //讀隊頭元素 virtual T getHead() const = 0; //虛析構函數 virtual ~Queue(){} }; #endif
隊列做爲一個特殊的線性表,天然也有着順序以及鏈式存儲兩種方式,咱們先來看看它的順序存儲方式——循環隊列
在隊列的順序存儲中,咱們除了建立一個具備必定空間的數組空間外,還須要兩個指針,分別指向隊列的前端和微端,下面的代碼中,咱們選擇將隊頭指針指向頭元素的前一個位置,隊尾指針指向隊尾元素(固然這不是惟一的方式,還能夠將頭指針指向頭元素,隊尾指針指向隊尾元素的後一個位置,原理是基本一致的)
爲何要這麼作,而且爲何這種存儲咱們叫作循環隊列?
咱們一步步分析一下:
咱們先按照咱們通常的想法畫出隊列元素進出隊的過程,例如隊列元素出隊
這樣的設想,也就是根據咱們前面食堂排隊的例子畫出來的,可是咱們能夠清晰的看到,當a0出隊後,a0後的元素所有須要前移,將空位補上,但咱們在計算機中講究性能二字,如何能夠提升出隊的性能呢?
循環隊列就這樣被設計出來了,咱們若是再也不限制隊頭必定在整個空間的最前面,咱們的元素也就不須要集體移動了
這個時候咱們就須要考慮這樣的問題了:
① 如何爲了解決只有一個元素的時候,隊頭和隊尾重合使得處理變得麻煩?
可是有一個大問題出現了 !
若是前面有空閒的空間還好說,一旦頭元素前面沒有空間,咱們的隊頭指針就指向到了數組以外,也就會出現數組越界問題,這該怎麼辦呢?
咱們能夠看到,雖然咱們的表頭已經沒有了任何空間,可是表的後半部分還有空餘空間,這種現象咱們稱做假溢出,打個比方,接近上課你緩緩走進教室,看到只有前排剩下了兩個位置,你總不會轉身就走吧,固然能夠去前排坐,只有實在沒座位了,才考慮離開
咱們能夠作出這樣一種比較可行的方案
咱們剛纔也提到了,當表頭指針和表尾指針相等的時候就解決了空隊列的狀況,可是在表滿的狀況下,你會發現,一樣也知足表頭表尾指針相等,那麼又如何解決這個問題呢?(咱們給出三種可行的解決方案)
咱們重點講解 C 中的方法
咱們根據這種方法能夠總結出幾個條件的運算式
隊列爲滿的條件:(rear+1) % MaxSize == front
front == rear
(rear- front + maxSize) % MaxSize
rear = (rear + 1) % maxSize
出隊:front = (front + 1) % maxSize
#ifndef _SEQQUEUE_H_ #define _SEQQUEUE_H_ #include "Queue.h" template <class T> class seqQueue:public Queue<T> { private: //指向存放元素的數組 T &data; //隊列的大小 int maxSize; //定義隊頭和隊尾指針 int front, rear; //擴大隊列空間 void resize(); public: seqQueue(int initSize = 100); ~seqQueue() {delete []data;} //清空隊列 void clear() {front = rear = -1;} //判空 bool empty() const {return front == rear;} //判滿 bool full() const {return (rear + 1) % maxSize == front;} //隊列長度 int size() const {(rear- front + maxSize) % maxSize;} //入隊 void enQueue(const T &x); //出隊 T deQueue(); //取隊首元素 T getHead() const; }; #endif
template <class T> seqQueue<T>::seqQueue(int initSize) { if (initSize <= 0) throw badSize(); data = new T[initSize]; maxSize = initSize; front = rear = -1; }
template <class T> void seqQueue<T>::enQueue(const T &x) { //隊滿則擴容 if ((rear + 1) % maxSize == front) resize(); //移動隊尾指針 rear = (rear + 1) % maxSize; //x 入隊 data[rear] = x; }
template <class T> T seqQueue<T>::deQueue() { //隊列爲空則拋出異常 if (empty()) throw outOfRange(); //移動隊尾指針 front = (front + 1) % maxSize; //x入隊 return data[front]; }
template <class T> T seqQueue<T>::getHead() const { if (empty()) throw outOfRange(); //返回隊首元素,不移動隊首指針 return data[(front + 1) % maxSize]; }
template <class T> void seqQueue<T>::resize() { T *p = data; data = new T[2 *maxSize]; for(int i = 1; i < size(); ++i) //複製元素 data[i] = p[(front + i) % maxSize]; //設置隊首和隊尾指針 front = 0; rear = size(); maxSize *= 2; delete p; }
用鏈式存儲結構表示隊列咱們叫作鏈隊列,用無頭結點的單鏈表表示隊列,表頭爲隊頭,表尾爲隊尾,須要兩個指針分別指向隊頭元素和隊尾元素,這種存儲結構的好處之一就是不會出現隊列滿的狀況
#ifndef _LINKQUEUE_H_ #define _LINKQUEUE_H_ #include <iostream> #include "Queue.h" template <class T> class linkQueue:public Queue<T> { private: struct node { T data; node *next; node (const T &x, node *N = NULL) { data = x; next = N; } node ():next(NULL){} ~node () {} }; node *front, *rear; public: linkQueue(){front = rear = NULL;}; ~linkQueue() {clear();} //清空隊列 void clear(); //判空 bool empty() const {return front == NULL;} //隊列長度 int size() const; //入隊 void enQueue(const T &x); //出隊 T deQueue(); //取隊首元素 T getHead() const; }; #endif
template <class T> void linkQueue<T>::clear() { node *p; //釋放隊列中全部節點 while(front != NULL) { p = front; front = front -> next; delete p; } //修改尾指針 rear = NULL; }
template <class T> int linkQueue<T>::size() const { node *p = front; int count = 0; while(p) { count++; p = p -> next; } return count; }
template <class T> void linkQueue<T>::enQueue(const T &x) { if(rear == NULL) front = rear = new node(x); else { rear -> next = new node(x); rear = rear -> next; } }
template <class T> T linkQueue<T>::deQueue() { //隊列爲空則拋出異常 if (empty()) throw outOfRange(); node *p = front; //保存隊首元素 T value = front -> data; front = front -> next; if (front == NULL) rear = NULL; delete p; return value; }
template <class T> T linkQueue<T>::getHead() const { if (empty()) throw outOfRange(); return front -> data; }
若是文章中有什麼不足,或者錯誤的地方,歡迎你們留言分享想法,感謝朋友們的支持!
若是能幫到你的話,那就來關注我吧!若是您更喜歡微信文章的閱讀方式,能夠關注個人公衆號
在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤
一個堅持推送原創開發技術文章的公衆號:理想二旬不止