五、佇列(Queue)
阿新 • • 發佈:2018-12-05
一、概述
佇列(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++佇列的方法
- back()——返回最後一個元素;
- empty()——如果佇列空則返回true,否則返回false;
- front()——返回第一個元素;
- pop()——從隊頭刪除第一個元素;
- push()——在隊尾插入一個元素;
- 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原子操作,可以實現非常高效的併發佇列