隊列的一些概念梳理及題目

目錄

一、隊列基本概念

二、順序隊列

三、循環隊列

四、鏈隊列

五、雙端隊列

六、題



一、隊列基本概念

像棧一樣,隊列(queue)也是一種線性表,它的特性是先進先出,插入在一端,刪除在另一端。就像排隊一樣,剛來的人入隊(push)要排在隊尾(rear),每次出隊(pop)的都是隊首(front)的人。

  1. 隊頭:允許刪除的一端
  2. 隊尾:允許插入的一端

 

二、順序隊列

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;
  1. 當Q.rear == NULL且Q.rear == NULL時,鏈式隊列爲空。
  2. 不帶頭結點的鏈式隊列在操作上往往比較麻煩,因此通常將鏈式隊列設計成帶(頭結點的單鏈表),這樣插入和刪除操作就統一了。
  3. 用單鏈表表示的鏈式隊列特別適合於數據元素變動比較大的情形,而且不存在隊列滿而且溢出的問題。假如程序中需要多個隊列,與多個棧的情況一樣,最好使用鏈式隊列,這樣就不會出現存儲分配不合理和“溢出”的問題

鏈式隊列的基本操作

(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;屬實不小心,一定要小心!記住!!

 明日繼續更~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章