目錄
一、隊列基本概念
像棧一樣,隊列(queue)也是一種線性表,它的特性是先進先出,插入在一端,刪除在另一端。就像排隊一樣,剛來的人入隊(push)要排在隊尾(rear),每次出隊(pop)的都是隊首(front)的人。
- 隊頭:允許刪除的一端
- 隊尾:允許插入的一端
二、順序隊列
1.隊列的順序存儲類型:
#define MaxSize 50
typedef struct{
ElemType data[MaxSize];
int front, rear;
}SqQueue;
初始狀態(隊空條件):
Q.front == Q.rear == 0;
2.front 和 rear分別指示隊頭和隊尾元素的位置:
front指向隊頭,rear指向隊尾的後一位置(一般默認情況),或者front指向隊頭的前一位置,rear指向隊尾。
- 進隊操作:隊不滿時,先送值到隊尾元素,再將隊尾指針加一
- 出隊操作:隊不空時,先取隊頭元素值,再將隊頭指針加一
注意:Q.rear == MaxSize不能作爲隊列滿的條件,因爲有可能存在假溢出(上溢出):
比如說5個元素入隊,出來4個元素,這時隊列裏面僅剩下一個元素,但是由於整體上元素向前移動了4個格,最後一個元素就頂在了最上面一個格,隊列其實好幾個格都是空的,但是另一個層面來講卻已經滿了,所以稱作這種情況爲假溢出。
爲了避免這種假溢出,我們下面來看循環隊列↓↓↓
三、循環隊列
將順序隊列臆造爲一個環裝的空間,即把存儲隊列元素的表從邏輯上視爲一個環。當隊首指針Q.front = Maxsize - 1後,再前進一個位置就自動到0,這可以用除餘來實現。
- 初始時:Q.front = Q.rear = 0
- 隊首指針進1:Q.front = (Q.front+1) % Maxsize;
- 隊尾指針進1:Q.rear = (Q.rear+1) % MaxSize
- 隊列長度:(Q.rear - Q.front + MaxSize)% MaxSize
出隊入隊時,指針都按照順時針方向進一。
隊空和隊滿都會Q.front ==Q.rear,爲了區分隊空和隊滿,有以下三種方式:
1.犧牲一個單元來區分隊空和隊滿:隊頭指針在隊尾指針的下一位置作爲隊滿的標誌
- 隊滿條件:(Q.rear + 1)% MaxSize == Q.front
- 隊空條件仍:Q.front == Q.rear
- 隊列中元素的個數:(Q.rear - Q.front + MaxSize)% MaxSize
2.類型中添加:
- 元素個數:比如說結構體中添加size:size == 8個元素,size == 0空。
- 是否滿了:比如說結構體中添加flag:flag == 0空,flag == 1滿。
循環隊列的操作
(1)循環隊列初始化
//循環隊列初始化
void InitQueue(SqQueue &Q){
Q.rear = Q.front = 0;
}
(2)循環隊列判隊空
//循環隊列判隊空
bool isEmpty(SqQueue Q){
if(Q.rear == Q.front) return true;
else return false;
}
(3)循環隊列入隊
//循環隊列入隊
bool EnQueue(SqQueue &Q, ElemType x){
if((Q.rear + 1) % MaxSize == Q.front) return false; //隊滿
Q.data[Q.rear] = x;
Q.rear = (Q.rear + 1) % MaxSize; //隊尾指針加1取模
return true;
}
(4)循環隊列出隊
//循環隊列出隊
bool DeQueue(SqQueue &Q, ElemType &x){
if(Q.rear == Q.front) return false; //隊空
x = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize; //隊頭指針加1取模
return true;
}
四、鏈隊列
隊列的鏈式存儲稱爲鏈隊列,實際上是同時帶有頭指針和尾指針的單鏈表。頭指針指向隊頭結點,尾指針指向隊尾結點,即單鏈表的最後一個結點。
//隊列的鏈式存儲類型
typedef struct{
ElemType data; //結點數據
struct LinkNode *next; //結點指針
}LinkNode;
typedef struct{
LinkNode *front, *rear; //隊列包含front結點和rear結點
}LinkQueue;
- 當Q.rear == NULL且Q.rear == NULL時,鏈式隊列爲空。
- 不帶頭結點的鏈式隊列在操作上往往比較麻煩,因此通常將鏈式隊列設計成帶(頭結點的單鏈表),這樣插入和刪除操作就統一了。
- 用單鏈表表示的鏈式隊列特別適合於數據元素變動比較大的情形,而且不存在隊列滿而且溢出的問題。假如程序中需要多個隊列,與多個棧的情況一樣,最好使用鏈式隊列,這樣就不會出現存儲分配不合理和“溢出”的問題。
鏈式隊列的基本操作
(1)鏈式隊列初始化:新建一個頭結點,兩指針均指向此結點,結點後指空。
//鏈式隊列初始化
void InitQueue(LinkQueue &Q){
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;//只有一個頭結點
}
(2)鏈式隊列判空:Q.rear == Q.front就是空
//循環隊列判隊空
bool isEmpty(SqQueue Q){
if(Q.rear == Q.front) return true;
else return false;
}
(3)鏈式隊列入隊:
- 創建新結點,將結點進行賦值:值爲x,指針指空
- 將新結點鏈接到鏈表尾,將表尾後移
//鏈式隊列入隊
void EnQueue(LinkQueue &Q, ElemType x){
LinkNode *s = (ListNode *)malloc(sizeof(LinkNode));//創建新結點
s->data = x; s->next = NULL;
Q.rear->next = s;
Q.rear = s;
}
(4)鏈式隊列出隊
- 判空,待刪除結點p在頭結點後,將p值賦給x(如果鏈表裏只有一個結點,即p是尾結點,那麼將尾結點指向頭結點,否則尾結點不用動),然後刪除掉p就完成啦!
//鏈式隊列出隊
bool DeQueue(LinkQueue &Q, ElemType &x){
if(Q.rear == Q.front) return false;
LinkNode *p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if(Q.rear == p) Q.rear = Q.front;
free(p);
return true;
}
五、雙端隊列
雙端隊列是指允許兩端都可以入隊和出隊操作的隊列。其元素的邏輯結構還是線性結構,將隊列的兩端分別稱爲前端和後端,兩端都可以入隊和出隊。
- 輸出受限的雙端對列:允許在一端進行插入和刪除,另一端只允許插入
- 輸入受限的雙端隊列:允許在一端進行插入和刪除,另一端只允許刪除
這裏需要做習題哦~明天繼續更新習題 [狗頭]
六、題目
(1)循環隊列存儲在數組A[0...n]中,入隊時的操作是
rear = (rear + 1) mod (n + 1);
隊滿條件是:
Q.front == (Q.rear + 1)% maxSize;
(2)已知循環隊列的存儲空間爲數組A[21],front指向隊頭元素的前一位置,rear指向隊尾元素,假設當前front和rear的值分別爲8和3,則該隊列的長度爲( )
隊列的長度爲(rear - front + maxSize)% maxSize = (3 - 8 + 21)% maxSize = 16;
所以答案 16
(3)若用數組A[0...5]來實現循環隊列,且當前rear和front的值分別爲1和5,當從隊列中刪除一個元素,再加入兩個元素後,rear和front的值爲( )
循環隊列中,每刪除一個元素,隊首指針front = (front + 1) % 6,每插入一個元素,隊尾指針rear = (rear + 1)% 6
上述操作後,front = 0, rear = 3。所以答案 3 和 0
(4)已知循環隊列存儲在一維數組A[0...n-1],且隊列非空時front和rear分別指向隊頭元素和隊尾元素。若初始時隊列爲空,且要求第一個進入隊列的元素存儲在A[0]處,則初始時 front 和 rear的值分別是( )
注意:一定要注意front和rear指向哪裏!到底是隊頭,隊尾?還是隊頭減一,隊尾?還是隊頭,隊尾加一?
根據題意,第一個元素進入隊列後存儲在A[0]處,此時 front 和 rear值都爲0。由於要執行(rear + 1)% n操作,所以如果入隊後指針指向0,則rear初值爲n-1,而由於第一個元素在 A[0]中,插入操作只改變rear指針,所以front爲0不變。所以答案 0,n-1;
(5)最適合用做鏈隊的鏈表是(),最不適合用做鏈隊的鏈表是( )
答案:帶隊首指針和隊尾指針的非循環鏈表、只帶隊首指針的非循環雙鏈表
原因:
- 如果用循環鏈表,因爲不涉及刪除最後一個結點,所以不需要循環,是多餘的!而在隊尾添加元素時還要修改成循環的,就更多餘了!
- 而下一個問題不適合做鏈隊的鏈表當然是這個沒有尾結點的啦,因爲每次刪除尾巴頭指針都要走好遠~
下面這種鏈表最適合做鏈隊啦嘻嘻↓↓↓
做這種題建議自己模擬一下~
(6)用鏈式存儲方式的隊列進行刪除操作時需要( )
答案:頭尾指針可能都要修改
哇!當隊列用鏈表形式存儲時,刪除元素時從隊頭刪除,一般情況下僅需要修改頭指針,但若此時隊列中僅有一個元素,隊尾指針也要被修改,因爲僅有一個元素時,刪除後隊列爲空,需修改尾指針爲rear = front;
(7)在一個鏈隊列中,假設隊頭指針爲 front,隊尾指針爲rear,x所指向的元素需要入隊,則需要執行的操作爲( )
答案:rear->next = x; x->next = null; rear = x;
唉當時錯選了rear->next = x; rear = x;屬實不小心,一定要小心!記住!!
明日繼續更~