1. 程式人生 > >五、佇列(Queue)

五、佇列(Queue)

一、概述

佇列(queue): 只允許在一端進行插入 (隊尾) 操作,而在另一端 (隊頭) 進行刪除操作的線性表。
隊頭:刪除操作的一端——front
隊尾:插入操作的一端——rear

特點:先進先出(First In First Out)
在這裡插入圖片描述
其他常用佇列:迴圈佇列、阻塞佇列、併發佇列。

二、佇列的抽象資料型別

ADT 佇列 (Queue)
Data
	同線性表。元素具有相同的型別,相鄰元素具有前驅和後繼關係
Opreation
	InitQueue(*Q):                初始化操作,建立一個空佇列Q。
	DestroyQueue(*Q):             若佇列Q存在,則銷燬它。
	ClearQueue(*Q):               將佇列Q清空。
	QueueEmpty(Q):                若佇列為空,返回true,否則返回false。
	GetHead(Q,*e):                若佇列Q存在且非空

二、C++佇列的方法

  1. back()——返回最後一個元素;
  2. empty()——如果佇列空則返回true,否則返回false;
  3. front()——返回第一個元素;
  4. pop()——從隊頭刪除第一個元素;
  5. push()——在隊尾插入一個元素;
  6. size()——返回佇列中元素的大小;
#include<iostream>
#include<queue>
using namespace std;

int main()
{
    queue<int> que;
    // 入隊
    for(int i =
0; i < 50; i++) { que.push(i); } cout<<"the size of queue:"<<que.size()<<endl; while(!que.empty()) { cout<<"the front element of queue:"<<que.front()<<" "; cout<<"the rear element of queue:"<<que.back()<<
endl; que.pop(); } cout<<"the size of queue:"<<que.size()<<endl; return 0; }

三、實現

1、順序佇列——陣列實現

  • 順序佇列需事先確定佇列的大小,不支援動態分配儲存空間,所以插入和刪除元素比較省時,但是會造成空間的浪費。

解決方法:迴圈佇列 =》解決空間浪費的問題
迴圈佇列的實現:

#include <iostream>
using namespace std;

const int MAXSIZE = 1000;
typedef int ELEMTYPE;
const int N = 10;
typedef struct{
    ELEMTYPE data[MAXSIZE];
    int head;   /*隊頭指標*/
    int rear;   /*隊尾指標*/
}Queue;

Queue Q;
void initQueue(Queue &Q);
void printQueue(Queue &Q);
bool isQueueEmpty(Queue &Q);
bool isQueueFull(Queue &Q);
bool EnQueue(Queue &Q, ELEMTYPE e);
bool DeQueue(Queue &Q, ELEMTYPE &e);

int main()
{
    for(int i = 0; i < N; i++)
    {
        EnQueue(Q, i);
    }
    printQueue(Q);
    return 0;
}


void initQueue(Queue &Q)
{
    Q.head = 0;
    Q.rear = 0;
}
void printQueue(Queue &Q)
{
    ELEMTYPE e;
    while(!isQueueEmpty(Q))
    {
        DeQueue(Q,e);
        cout<<e<<" ";
    }
    cout<<endl;
}
bool isQueueEmpty(Queue &Q)
{
    if(Q.head == Q.rear)
        return true;
    else
        return false;
}
bool isQueueFull(Queue &Q)
{
    if((Q.rear+1)%MAXSIZE == Q.head)
        return true;
    else
        return false;
}
bool EnQueue(Queue &Q, ELEMTYPE e)
{
    if(isQueueFull(Q))
        return false;
    Q.rear = (Q.rear+1)%MAXSIZE;
    Q.data[Q.rear] = e;
    return true;
}
bool DeQueue(Queue &Q, ELEMTYPE &e)
{
    if(isQueueEmpty(Q))
        return false;
    Q.head = (Q.head+1)%MAXSIZE;
    e = Q.data[Q.head];
    return true;
}

2、鏈式佇列——連結串列實現

  • 可以不需要事先知道佇列的大小,支援動態和釋放空間,但是插入和刪除操作比較耗時
#include <iostream>
using namespace std; 
struct NODE//雙向連結串列基本單元結構
{
    int data;
    NODE *next;//後繼指標
    NODE *pre;//前驅指標
}; 
class QUEUE//定義queue類封裝資料和實現
{
private:
    NODE *front;//隊頭指標
    NODE *tail;//隊尾指標
    unsigned size; 
public:
    QUEUE();
    ~QUEUE(){};
    void initialize();  //初始化
    void enqueue(int n); //入隊
    void dequeue();  //出隊
    int get_front();  //獲取元素
    void clear();  //清空佇列
    int get_size();  //返回元素個數
    bool isempty();  //判斷是否為空
    void display_queue(); //輸出佇列
}; 
QUEUE::QUEUE()
{
    initialize();
} 
void QUEUE::initialize()
{
    //初始化頭部和尾部指標
    front = new NODE();
    tail = new NODE();
    //將頭尾連線
    front->data = tail->data = 0;
    front->pre = tail->next = NULL;
    front->next = tail;
    tail->pre = front;
    size = 0;  //設定元素個數為0
} 
void QUEUE::enqueue(int n)
{
    //開闢新節點
    NODE *new_ele = new NODE();
    //設定資料
    new_ele->data = n; 
   //將新節點插入到雙向連結串列尾部
    tail->pre->next = new_ele; 
   new_ele->next = tail;
    new_ele->pre = tail->pre;
    tail->pre = new_ele;
     size++;  //元素個數加1
} 
void QUEUE::dequeue()
{
    if (isempty())//避開對空佇列的操作
    {
        cout << "queue is empty" << endl; 
        return;
    }
    //獲取刪除將要刪除的元素指標
    NODE *temp = front->next;
    front->next = temp->next;
    temp->next->pre = front; 
   delete(temp);//釋放記憶體
    size--;
}    
int QUEUE::get_front()
{
    if (front->next != tail)
        return front->next->data;
    else
        cout << "empty queque" << endl;
    return -1;
} 
void QUEUE::clear()
{
    NODE *temp = front;
    //遍歷連結串列釋放所有節點記憶體 
    while(temp != tail)
    {
        NODE *del_data = temp;
        temp = temp->next;
        delete(del_data);
        }
    //呼叫函式重新初始化
    initialize();
}
int QUEUE::get_size()
{
    return size;
} 
void QUEUE::display_queue()
{
    NODE *temp = front->next;
    while (temp != tail)
    {
        cout << temp->data << " ";
        temp = temp->next;
    }
    if (isempty())
        cout << "queue is empty" << endl;
    else
        cout << endl;
}
bool QUEUE::isempty()
{
    return front->next == tail;
}
int main(int argc, char const *argv[])
{
    QUEUE que; 
   /*     *do somthing here     */    
    return 0;
}

3、迴圈佇列

  • 關鍵:判斷佇列是空對還是滿隊
    • 空:head == tail
    • 滿:(tail+1)%n == head
  • 當迴圈佇列滿隊時,tail指標指向的位置實際並沒有儲存資料。=》迴圈佇列會浪費一個數組的儲存空間
 template<class T>
 class SeqQueue{
      protected:
         T *element;
         int front,rear;
         int maxSize;
     public:
         SeqQueue(int sz=10){
             front=rear=0;
             maxSize=sz;
             element=new T[maxSize];
         }
         ~SeqQueue(){
             delete[] element;
         }
         bool EnQueue(const T& x){//入隊 
             if(isFull()) return false;
             element[rear]=x;
             rear=(rear+1)%maxSize;
             return true;
         }
         bool DeQueue(T& x){//出隊 
             if(isEmpty()) return false;
             x=element[front];
             front=(front+1)%maxSize;
             return true;
         }
         bool getFront(T& x){//獲取隊首元素 
             if(isEmpty()) return false;
             x=element[front];
             return true;
         }
         void makeEmpty(){//佇列置空 
             front=rear=0;
         }
         bool isEmpty()const{//判斷佇列是否為空 
             return (rear==front)?true:false;
         }
         bool isFull()const{//佇列是否為滿
              return ((rear+1)%maxSize==front)?true:false;
         }
         int getSize()const{
             return (rear-front+maxSize)%maxSize;
         }
 }; 

4、阻塞佇列

支援阻塞操作的佇列。具體來講,支援阻塞新增和阻塞移除。

阻塞新增: 當佇列滿的時候,佇列會阻塞插入插入的元素的執行緒,直到佇列不滿;
阻塞移除: 在佇列為空時,隊裡會阻塞插入元素的執行緒,直到佇列不滿。

阻塞佇列常用於“生產者-消費者模型”,生產者是向佇列新增元素的執行緒;消費者是從佇列取元素的執行緒。

5、併發佇列

併發佇列就是佇列的操作多執行緒安全。
實現:基於陣列的迴圈佇列,利用CAS原子操作,可以實現非常高效的併發佇列