循環隊列的實現及細節


1. 隊列定義:

  • 一種可以實現 “先進先出” 的存儲結構(類似於排隊)
  • 只允許在一端插入元素,在另一端刪除元素,不可以混在一起

2. 隊列分類:

  • 鏈式隊列:由鏈表實現的隊列,本質是鏈表
  • 靜態隊列:由數組實現的隊列,本質是數組

3. 循環隊列講解

  1. 靜態隊列為什么必須時循環隊列:靜態隊列必須是循環隊列,這是由數組的特性決定的。隊列只允許尾部添加元素頭部刪除元素,如果用數組實現,添加元素時元素向后排列,刪除元素時前面的位置就會空出來,時間長了之后會造成大量的空間浪費,所以要使用循環隊列,以防止空間浪費

    非循環隊列,會浪費很多空間

    循環隊列,不會浪費空間
  2. 循環隊列需要幾個參數來確定:兩個,即隊列頭、尾(實質上就是數組的兩個下標)
  3. 循環隊列各個參數的含義:
    • 循環隊列需要兩個參數來確定,且這兩個參數在不同的情況下由不同的含義
    • 隊列初始化時:front和rear值都為零
    • 隊列非空時:front代表隊列第一個元素,rear代表隊列的最后一個有效元素的下一個元素
    • 隊列空時:front和rear相等單不一定為零
  4. 循環隊列入隊偽算法講解:將值存入下標為rear的位置后,rear = (rear+1) %數組長度,而不是 rear+1
  5. 循環隊列出隊偽算法講解:front = (front + 1) % 數組長度,與入隊相同
  6. 如何判斷循環隊列是否為空:若 rear == front 則隊列為空
  7. 如何判斷循環隊列是否為滿:若 (rear + 1)% 數組長度 == front 則隊列已滿(因為 rear 指示的是隊列最后一個有效元素的下一個元素,所以這樣判斷隊列已滿會浪費一個數組位置,但是這已經比非循環隊列省了很多空間了。如果要不浪費哪一個元素就需要多加一個參數,這樣就會麻煩很多,沒有必要)

4. 循環隊列實現及細節

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct Queue{
    int *pBase;                               //指向數組的指針
    int front;                                //隊列頭,數值尾隊列第一個有效元素的下標
    int rear;                                 //隊列尾,數值尾隊列最后一個有效元素的下一個元素的下標
}QUEUE,*PQUEUE;

PQUEUE InitQueue(void);
/**操作結果:構造一個空隊列Q */
void DestoryQueue(PQUEUE pQ);
/**初始條件:隊列已經存在
 * 操作結果:隊列被銷毀
 */
void ClearQueue(PQUEUE pQ);
/**初始條件:隊列已經存在
 * 操作結果:將隊列清空
 */
bool QueueEmpty(PQUEUE pQ);
/**初始條件:隊列已經存在
 * 操作結果:若隊列為空,返回TURE,否則返回FALSE
 */
bool QueueFull(PQUEUE pQ);
/**初始條件:隊列已存在
 * 操作結果:若隊列已滿,返回TURE,否則返回FALSE
 */
int QueueHead(PQUEUE pQ,int val);
/**初始條件:隊列存在且非空
 * 操作結果:返回隊列頭元素
 */
bool EnQueue(PQUEUE pQ,int val);
/**初始條件:隊列已存在
 * 操作結果:在隊尾插入元素
 */
bool DeQueue(PQUEUE pQ,int *pVal);
/**初始條件:隊列存在且非空
 * 操作結果:刪除隊頭元素,並返回其值
 */
void QueueTraverse(PQUEUE pQ);
/**初始條件:隊列存在且非空
 * 操作結果:輸出隊列元素
 */

int N;                                       //N 用於在各個函數之間傳遞隊列的實際長度

int main()
{
    int t;
    printf("請輸入隊列長度:");               //這里用 N + 1 是因為輸入的隊列長度是實際元素的個數,因為循環隊列會浪費一個元素,所以令隊列的實際長度為 N + 1 方便使用
    scanf("%d",&t);
    N=t+1;
    PQUEUE pQ=InitQueue();
    int i,n;
    for(i=0;i<N-1;i++){                      //用戶輸入的隊列長度為 N - 1
        scanf("%d",&n);
        EnQueue(pQ,n);
    }
    QueueTraverse(pQ);
    int val;
    DeQueue(pQ,&val);
    QueueTraverse(pQ);
    printf("出隊元素為:%d",val);
}

PQUEUE InitQueue(void)                       //構造一個空隊列
{
    PQUEUE pQ=(PQUEUE)malloc(sizeof(QUEUE)); //這里必須用 malloc 函數,否則生成的就是一個局部變量。在這里錯了好幾次,應當注意!!
    pQ->pBase=(int *)malloc(sizeof(int)*N);
    pQ->rear=0;                              //因為這是一個空隊列,其中沒有任何有效元素,所以 rear 和 front 的值都為零
    pQ->front=0;
    return pQ;
}

void DestoryQueue(PQUEUE pQ)                 //銷毀隊列
{
    free(pQ);
}

void ClearQueue(PQUEUE pQ)                   //清空隊列
{
    pQ->front=pQ->rear;                      //清空隊列時並不需要將隊列里所有元素都重置為零,只需要令 front 與 rear 的值相同即可,以后入隊時輸入的值就會把原來的值覆蓋
}

bool QueueEmpty(PQUEUE pQ)                   //判斷隊列是否為空
{
    if(pQ->front==pQ->rear){
        return true;
    }else{
        return false;
    }
}

bool QueueFull(PQUEUE pQ)                    //判斷隊列是否已滿
{
    int m,n;
    m=pQ->rear;
    n=pQ->front;
    if((m+1)%N==(n)) {
        return true;
    }else{
        return false;
    }
}

int QueueHead(PQUEUE pQ,int val)              //返回隊列頭元素
{
    val=pQ->pBase[pQ->front];
    return val;
}

bool EnQueue(PQUEUE pQ,int val)               //在隊尾插入元素
{
    if(QueueFull(pQ)){
        return false;
    }else{
        pQ->pBase[pQ->rear]=val;
        pQ->rear=(pQ->rear+1)%N;
        return true;
    }
}

bool DeQueue(PQUEUE pQ,int *pVal)             //刪除隊頭元素並返回其值
{
    if(QueueEmpty(pQ)){
        return false;
    } else{
        *pVal=pQ->pBase[pQ->front];           //這里因為需要函數類型為 bool ,所以無法正常返回 int 類型的值,所以用指針直接修改 val 的值即可
        pQ->front=(pQ->front+1)%N;
        return true;
    }
}

void QueueTraverse(PQUEUE pQ)                 //遍歷輸出隊列元素
{
    int i=pQ->front;
    while(i!=pQ->rear){
        printf("%d ",pQ->pBase[i]);
        i=(i+1)%N;
    }
    printf("\n");
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM