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

1、隊列是什麼

隊列是一種能夠實現「先進先出」的存儲結構。數組

隊列一般能夠分爲兩種類型:函數

1、順序隊列,採用順序存儲,當長度肯定時使用。 順序隊列又有兩種狀況:spa

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

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

2、鏈式隊列,採用鏈式存儲,長度不肯定時使用(由鏈表實現)。對象

因爲鏈式隊列跟鏈表差很少,因此在這裏只針對循環(環形)隊列來講明並實踐。
循環隊列的兩個參數:
  ①front,front指向隊列的第一個元素。(front==head)
  ②rear,rear指向隊列的最後一個有效元素的下一元素。(rear==tail)blog


隊列的兩個基本操做:出隊和入隊。索引

 

2、隊列的結構

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

    

 

3、隊列的操做

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

在循環隊列中,「隊滿」和「隊空」的條件有多是相同的,都是front ==rear,這種狀況下,沒法區別是「隊滿」仍是「隊空」。

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

(1)另設一個標誌以區別是「隊滿」仍是「隊空」。(即入隊/出隊前檢查是否「隊滿」/「隊空」)

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

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

 

 

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

 

 

 

從上圖能夠看出,隨着入隊、出隊的進行,會使整個隊列總體向後移動,就會出現上圖中的現象:隊尾指針已經移到了最後,即隊尾出現溢出,沒法再進行入隊操做,然而實際上,此時隊列中還有空閒空間,這種現象稱爲「假溢出」。

解決「假溢出」的三種辦法:

  • 方法一:每次刪除隊頭元素後,把整個隊列向前移動一個位置,這樣可保證隊頭元素在存儲空間的最前面。但每次刪除元素時,都要把表中全部元素向前移動,效率過低。
  • 方法二:當隊尾指針出現溢出時,判斷隊頭指針位置,若是前部有空閒空間,則把當前隊列總體前移到最前方。這種方法移動元素的次數大爲減小。
  • 方法三:將隊列當作頭尾相接的循環結構,當隊尾指針到隊尾後,再從隊頭開始向後指,這樣就不須要移動隊列元素了,顯然,第三種方法最經濟、應用最多,這種順序隊列被稱爲「循環隊列」或「環形隊列」。

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

所以入隊時隊尾指針加1操做改成: pQueue->tail = (pQueue->tail+1) % MAX_SIZE;

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

 

5、動態順序隊列的實現

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

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

/*1、使用數組存儲隊列的稱爲靜態順序隊列
 *2、使用動態分配的指針的稱爲動態順序隊列*/
// 【這裏的是動態順序隊列】
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;
}
相關文章
相關標籤/搜索