數據結構之順序隊列(C實現)


一、隊列是什么

隊列是一種可以實現“先進先出”的存儲結構。

隊列通常可以分為兩種類型:

一、順序隊列,采用順序存儲,當長度確定時使用。 順序隊列又有兩種情況:

  ①使用數組存儲隊列的稱為靜態順序隊列。

  ②使用動態分配的指針的稱為動態順序隊列。

二、鏈式隊列,采用鏈式存儲,長度不確定時使用(由鏈表實現)。

由於鏈式隊列跟鏈表差不多,所以在這里只針對循環(環形)隊列來說明並實踐。
循環隊列的兩個參數:
  ①front,front指向隊列的第一個元素。(front==head)
  ②rear,rear指向隊列的最后一個有效元素的下一元素。(rear==tail)


隊列的兩個基本操作:出隊和入隊。

 

二、隊列的結構

下面是一個循環隊列(基於數組實現)的結構圖:

    

 

三、隊列的操作

入隊(尾部入隊) 
  ①將值存入rear所代表的位置。
  ②rear = (rear+1)%數組的長度。
出隊(頭部出隊) 
  front = (front+1)%數組的長度。
隊列是否為空   
  front和rear的值相等,則該隊列就一定為空。
隊列是否已滿

在循環隊列中,“隊滿”和“隊空”的條件有可能是相同的,都是front ==rear,這種情況下,無法區別是“隊滿”還是“隊空”。

針對這個問題,有3種可能的處理方法: 【這里采用了第3種處理方法】

(1)另設一個標志以區別是“隊滿”還是“隊空”。(即入隊/出隊前檢查是否“隊滿”/“隊空”)

(2)設一個計數器,此時甚至還可以省去一個指針。

(3)少用一個元素空間,即約定隊頭指針在隊尾指針的下一位置時就作為“隊滿”的標志,即“隊滿”條件為:(pQueue->rear+1)%MAX_SIZE == pQueue->front。

 

 

四、隊列的一些問題以及解決辦法

 

 

 

從上圖可以看出,隨着入隊、出隊的進行,會使整個隊列整體向后移動,就會出現上圖中的現象:隊尾指針已經移到了最后,即隊尾出現溢出,無法再進行入隊操作,然而實際上,此時隊列中還有空閑空間,這種現象稱為“假溢出”。

解決“假溢出”的三種辦法:

  • 方法一:每次刪除隊頭元素后,把整個隊列向前移動一個位置,這樣可保證隊頭元素在存儲空間的最前面。但每次刪除元素時,都要把表中所有元素向前移動,效率太低。
  • 方法二:當隊尾指針出現溢出時,判斷隊頭指針位置,如果前部有空閑空間,則把當前隊列整體前移到最前方。這種方法移動元素的次數大為減少。
  • 方法三:將隊列看成頭尾相接的循環結構,當隊尾指針到隊尾后,再從隊頭開始向后指,這樣就不需要移動隊列元素了,顯然,第三種方法最經濟、應用最多,這種順序隊列被稱為“循環隊列”或“環形隊列”。

采用了這種頭尾相接的循環隊列后,入隊的隊尾指針加1操作及出隊的隊頭指針加1操作必須做相應的修改,以確保下標范圍為0~Max_Size-1。對指針進行取模運算,就能使指針到達最大下標位置后回到0,符合“循環”隊列的特點。

因此入隊時隊尾指針加1操作改為: pQueue->tail = (pQueue->tail+1) % MAX_SIZE;

入隊時隊尾指針加1操作改為: pQueue->head = (pQueue->head+1) % MAX_SIZE;

 

五、動態順序隊列的實現

基於數組的動態順序循環隊列的具體實現:

5.1  MyQueue.h

#ifndef MYQUEUEC_H
#define MYQUEUEC_H

#include <stdio.h>
#include <malloc.h>

/* 隊列: 只允許在表的一端(隊尾rear)進行插入操作,而在另一端(隊頭front)進行刪除操作的線性表
 * 插入操作簡稱為入隊  刪除操作簡稱為出隊   隊列具有先進先出的特點
 */

/*=====隊列的入隊、出隊示意圖========
 *
 *  出隊 ----------------- 入隊
 *   <--- a1,a2,a3,...,an <---
 *      -----------------
 *
 *================================*/

typedef enum
{
    OK=0, //正確
    ERROR=1,   //出錯
    TRUE=2,  //為真
    FALSE=3   //為假
}status;

typedef int ElemType;   //宏定義隊列的數據類型
#define MAX_SIZE 20

/*一、使用數組存儲隊列的稱為靜態順序隊列
 *二、使用動態分配的指針的稱為動態順序隊列*/
// 【這里的是動態順序隊列】
typedef struct
{
    ElemType *pBase;    //數組指針
    ElemType front;      //隊頭索引
    ElemType rear;       //隊尾索引
    int maxSize;    //當前分配的最大容量
}queue;

//創建空隊列 queueCapacity-隊列容量
status initQueue(queue *PQueue,int queueCapacity);
//銷毀隊列
void destroyQueue(queue *PQueue);
//清空隊列
void clearQueue(queue *PQueue);
//判斷隊列是否為空
status isEmpityQueue(queue *PQueue);
//判斷隊列是否為滿
status isFullQueue(queue *PQueue);
//獲得隊列長度
int getQueueLen(queue *PQueue);
//新元素入隊 [先進先出原則:在隊尾的位置插入] element-要插入元素
status enQueue(queue *PQueue,ElemType element);
//新元素出隊,同時保存出隊的元素 [先進先出原則:在隊頭的位置刪除]
status deQueue(queue *PQueue,ElemType *pElement);
//遍歷隊列
void queueTraverse(queue *PQueue);

#endif // MYQUEUEC_H

 

5.2  MyQueue.c

#include "myqueuec.h"

/* 隊列: 只允許在表的一端(隊尾rear)進行插入操作,而在另一端(隊頭front)進行刪除操作的線性表
 * 插入操作簡稱為入隊  刪除操作簡稱為出隊   隊列具有先進先出的特點
 */

/*=====隊列的入隊、出隊示意圖========
 *
 *  出隊 ----------------- 入隊
 *   <--- a1,a2,a3,...,an <---
 *      -----------------
 *
 *================================*/

//創建隊列 queueCapacity-隊列容量
status initQueue(queue *PQueue,int queueCapacity)
{
    //給數組指針分配內存
    PQueue->pBase = (ElemType *)malloc(sizeof(ElemType)*queueCapacity);
    if(!PQueue->pBase)
    {
        printf("給數組指針分配內存失敗\n");
        return ERROR;
    }

    PQueue->front = 0; //最開始創建時,隊頭索引為0
    PQueue->rear = 0; //最開始創建時,隊尾索引為0
    PQueue->maxSize = queueCapacity;

    return OK;
}

//銷毀隊列
void destroyQueue(queue *PQueue)
{
    free(PQueue);  //釋放隊列數組指針指向的內存
    PQueue = NULL;    //隊列數組指針重新指向NULL,避免成為野指針
}

//清空隊列
void clearQueue(queue *PQueue)
{
    PQueue->front = 0; //隊頭索引清0
    PQueue->rear = 0; //隊尾索引清0
}

//判斷隊列是否為空
status isEmpityQueue(queue *PQueue)
{
    if( PQueue->front == PQueue->rear )  //隊頭==隊尾,說明為空
        return TRUE;

    return FALSE;
}

/*
 *在循環隊列中,“隊滿”和“隊空”的條件有可能是相同的,都是front==rear,
 *這種情況下,無法區別是“隊滿”還是“隊空”。
 *針對這個問題,有3種可能的處理方法:
 *(1)另設一個標志以區別是“隊滿”還是“隊空”。(即入隊/出隊前檢查是否“隊滿”/“隊空”)
 *(2)設一個計數器,此時甚至還可以省去一個指針。
 *(3)少用一個元素空間,即約定隊頭指針在隊尾指針的下一位置時就作為“隊滿”的標志,
 *即“隊滿”條件為:(PQueue->rear+1)%MAX_SIZE == PQueue->front。
 *  【這里采用了第3種處理方法】
 */
//判斷隊列是否為滿
status isFullQueue(queue *PQueue)
{
    if( (PQueue->rear+1)%PQueue->maxSize == PQueue->front )  //隊列滿
        return TRUE;

    return FALSE;
}

//獲得隊列長度
int getQueueLen(queue *PQueue)
{
    //正常情況下,隊列長度為隊尾隊頭指針之差,但如果首尾指針跨容量最大值時,要%
    return (PQueue->rear - PQueue->front + PQueue->maxSize)%PQueue->maxSize;
}

//新元素入隊 [先進先出原則:在隊尾的位置插入] element-要插入元素
status enQueue(queue *PQueue,ElemType element)
{
    if(isFullQueue(PQueue)==TRUE)
    {
        printf("隊列已滿,不能再插入元素了!\n");
        return FALSE;
    }

    //向隊列中添加新元素
    PQueue->pBase[PQueue->rear] = element;
    PQueue->rear = (PQueue->rear+1) % PQueue->maxSize; //將rear賦予新的合適的值

    return TRUE;
}

//新元素出隊,同時保存出隊的元素 [先進先出原則:在隊頭的位置刪除]
status deQueue(queue *PQueue,ElemType *pElement)
{
    //如果隊列為空,則返回false
    if(isEmpityQueue(PQueue)==TRUE)
    {
        printf("隊列為空,出隊失敗!\n");
        return FALSE;
    }

    *pElement = PQueue->pBase[PQueue->front];       //先進先出
    PQueue->front = (PQueue->front+1) % PQueue->maxSize; //移到下一位置

    return TRUE;
}

//遍歷隊列
void queueTraverse(queue *PQueue)
{
    int i = PQueue->front;           //從頭開始遍歷
    printf("遍歷隊列:\n");
    while(i != PQueue->rear)     //如果沒有到達rear位置,就循環
    {
        printf("%d  ", PQueue->pBase[i]);
        i = (i+1) % PQueue->maxSize;              //移到下一位置
    }
    printf("\n");
}

 

5.3  main.c

#include <stdio.h>
#include "myqueuec.h"

int main(void)
{
    int value;          //用於保存出隊的元素
    //創建隊列對象
    queue *PQueue = (queue *)malloc(sizeof(queue));
    if(!PQueue->pBase)
    {
        printf("給隊列對象分配內存失敗\n");
        return -1;
    }

    //調用初始化隊列的函數
    initQueue(PQueue,MAX_SIZE);
    //調用出隊函數
    enQueue(PQueue, 1);
    enQueue(PQueue, 2);
    enQueue(PQueue, 3);
    enQueue(PQueue, 4);
    enQueue(PQueue, 5);
    enQueue(PQueue, 6);
    enQueue(PQueue, 7);
    enQueue(PQueue, 8);
    //調用遍歷隊列的函數
    queueTraverse(PQueue);
    //調用出隊函數
    if(deQueue(PQueue, &value))
    {
        printf("出隊一次,元素為:%d\n", value);
    }
    queueTraverse(PQueue);
    if(deQueue(PQueue, &value))
    {
        printf("出隊一次,元素為:%d\n", value);
    }
    queueTraverse(PQueue);

    free(PQueue);
    PQueue = NULL;

    getchar();
    return 0;
}

 


免責聲明!

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



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