棧與隊列(二)

在上一篇文章中悯辙,我們介紹了自定義的鏈式棧結(jié)構(gòu)及其接口的實現(xiàn)方式。這篇文章里货裹,我們來介紹如何實現(xiàn)自定義的順序隊列嗤形。

順序隊列結(jié)構(gòu)定義

在順序隊列中喉镰,我們采用一維數(shù)組進行存儲隊列元素浓体,為充分利用內(nèi)存空間崇众,我們采取 循環(huán)隊列 形式對元素進行組織管理嘉涌。

圖1. 循環(huán)隊列結(jié)構(gòu)

我們首先給出順序結(jié)構(gòu)的定義及其接口聲明蒜鸡,如下代碼所示:

#ifndef _SQQUEUE_H_
#define _SQQUEUE_H_

#include <cstdio>
#include <cstdlib>
#include <cstring>

typedef int POSITION;

struct stQueue {
    size_t MAXNUM;
    size_t length;
    ELEMTYPE *buffer;
    POSITION head, rear;
};

typedef struct stQueue * Psqueue;

Psqueue create(int num);
bool enqueue(Psqueue queue, ELEMTYPE x);
bool dequeue(Psqueue queue);
bool front(Psqueue queue, ELEMTYPE &x);
bool clear(Psqueue queue);
bool length(Psqueue queue);
bool isEmpty(Psqueue queue);
bool isFull(Psqueue queue);
bool destroy(Psqueue &queue);
bool print(Psqueue queue);
#endif

sqqueue.h 頭文件中灵疮,給出了隊列的結(jié)構(gòu)體定義敢课,該結(jié)構(gòu)包含如下結(jié)構(gòu)體成員變量:

  • MAXNUM:表示隊列元素緩沖區(qū)長度快压,亦即表示隊列最多可容納元素個數(shù)拯田;
  • length:表示隊列中當(dāng)前元素個數(shù)历造,length <= MAXNUM;
  • buffer:表示隊列用于存儲元素的緩沖區(qū)首地址船庇;
  • head:表示隊頭在緩沖區(qū)的下標吭产;
  • rear:表示隊尾在緩沖區(qū)的下標;

判斷循環(huán)隊列 隊滿鸭轮、 隊空 條件分別為:

  • 隊滿:(rear + 1)%MAXNUM == head
  • 隊空:head == rear

或者根據(jù) stqueue 結(jié)構(gòu)體中 lengthMAXNUM的關(guān)系進行判斷:

  • 隊滿:length == MAXNUM
  • 隊空:length == 0

順序隊列各接口實現(xiàn)

下面我們來依次介紹順序循環(huán)隊列的各接口實現(xiàn)臣淤。

創(chuàng)建順序隊列-create接口

創(chuàng)建順序隊列操作如下圖所示,可以分為兩步:

圖2. 創(chuàng)建順序隊列
  1. 分配順序隊列結(jié)構(gòu)體空間窃爷,并初始化結(jié)構(gòu)體成員邑蒋;
  2. 分配順序隊列元素存儲空間;

分配順序隊列結(jié)構(gòu)體以及分配順序表元素存儲空間按厘,均通過動態(tài)內(nèi)存分配函數(shù) malloc 進行操作即可医吊;此外,初始化結(jié)構(gòu)體成員亦比較簡單刻剥,length 初始化為 0遮咖,headrear也均初始化為 0造虏。

#include "sqqueue.h"
#include <iostream>
using namespace std;

Psqueue create(int num)
{
    Psqueue queue = (Psqueue) malloc(sizeof(struct stQueue));

    if (queue != NULL)
    {
        queue->buffer = (ELEMTYPE *) malloc(sizeof(ELEMTYPE)*num);
        if (queue->buffer != NULL)
        {
            queue->MAXNUM = num;
            queue->length = 0;
            queue->head = 0;
            queue->rear = 0;
            return queue;
        }

        free(queue);
    }
    
    return NULL;
}

以上代碼對應(yīng)文件 sqqueue.cpp御吞,需要注意的是,初始化 headrear 下標均為 0漓藕;此外陶珠,當(dāng)分配 buffer 緩沖區(qū)失敗時,我們需要將第1步分配的順序隊列結(jié)構(gòu)體空間也釋放掉享钞。

順序隊列判空-isEmpty接口

在實現(xiàn)順序隊列 入隊(enqueue)揍诽、出隊(dequeue) 操作前诀蓉,我們先來實現(xiàn)順序隊列的 判空(isEmpty)判滿(isFull) 操作。

根據(jù)本文前面的討論暑脆,判斷隊列為空的條件可以是:

head == rearlength == 0

以下為 isEmpty 操作接口的實現(xiàn):

bool isEmpty(Psqueue queue)
{
    if (queue == NULL)
    {
        return false;
    }
    
    // method 1
    //return queue->length == 0;
    // method 2
    return queue->head == queue->rear;
}

注: 當(dāng)傳遞的 queue 指針為空時渠啤,需返回為 true,大家可以思考下為什么添吗。

順序隊列判滿-isFull接口

順序隊列判滿操作的條件是:

(rear + 1)%MAXNUM == headlength == MAXNUM

兩個條件是有所區(qū)別的沥曹,第一個條件下,實際上隊列元素緩沖區(qū)還剩余 1 個元素存儲空間碟联;第二個條件下妓美,緩沖區(qū)所有元素存儲空間都被充分利用。

以下是 isFull 操作接口的實現(xiàn):

bool isFull(Psqueue queue)
{
    if (queue == NULL)
    {
        return true;
    }
    
    // method 1
    //return queue->length == queue->MAXNUM;
    // method 2
    return (queue->rear+1)%queue->MAXNUM == queue->head;
}

順序隊列入隊-enqueue操作

在順序隊列為非滿時鲤孵,順序隊列才可進行入隊操作壶栋。

此外,由 圖1圖2 可知 rear指向的緩沖區(qū)當(dāng)前為隊尾普监,且該位置尚未存儲元素贵试。因此,元素入隊時鹰椒,先將元素存儲在 rear 指向的緩沖區(qū)中锡移,然后再改變 rear 指向新隊尾。

bool enqueue(Psqueue queue, ELEMTYPE x)
{
   if (queue == NULL)
   {
       return false;
   }
   
   if (isFull(queue))
   {
       return false;
   }
   
   queue->buffer[queue->rear] = x;
   queue->rear = (queue->rear + 1) % queue->MAXNUM;
   queue->length = queue->length + 1;

   return true;
}

元素入隊后漆际,改變 rear 隊尾下標時,需考慮 rear + 1 >= MAXNUM 的情況(可通過 % 取模運算實現(xiàn))夺饲。

順序隊列出隊-dequeue操作

與入隊操作相反奸汇,當(dāng)順序隊列為非空時,才能進行元素出隊操作往声。

元素出隊后擂找,需要更新 head 隊頭下標,往后移動一個位置浩销,同時也需要考慮當(dāng) head 已經(jīng)在緩沖區(qū)尾部時進行 +1 操作需要將 head 重新移動到緩沖區(qū)頭部(可通過 % 取模運算實現(xiàn))贯涎。

bool dequeue(Psqueue queue)
{
    if (queue == NULL)
    {
        return false;
    }
    
    if (isEmpty(queue))
    {
        return false;
    }
    
    queue->length = queue->length - 1;
    queue->head = (queue->head + 1) % queue->MAXNUM;
    
    return true;
}

取隊頭元素-front操作

當(dāng)順序隊列不為空時,可以取當(dāng)前隊列隊頭元素慢洋。取隊頭元素并不影響當(dāng)前順序隊列狀態(tài)塘雳。

首先,來看看 front 接口操作聲明:

bool front(Psqueue queue, ELEMTYPE &x);

front 操作中我們使用了C++語言的 引用類型 存放順序隊列隊頭元素普筹。當(dāng) front 操作的返回值為 true 時败明,ELEMTYPE 類型的引用變量才被賦值為順序隊列隊頭元素。

以下是 front 接口操作的實現(xiàn):

bool front(Psqueue queue, ELEMTYPE &x)
{
    if (queue == NULL)
    {
        return false;
    }
    
    if (isEmpty(queue))
    {
        return false;
    }
    
    x = queue->buffer[queue->head];

    return true;
}

完成上述幾個接口后太防,應(yīng)該可以編寫測試代碼對其進行測試了(盡可能早的對項目進行測試)妻顶,由于篇幅的原因,這里就不在贅述。

清空隊列-clear接口

由于是順序隊列讳嘱,因此清空隊列只需要對隊列的 head幔嗦、 rear 下標進行更新即可,以下給出 clear 接口操作的實現(xiàn):

bool clear(Psqueue queue)
{
    if (queue == NULL)
    {
        return false;
    }
    
    queue->head = 0;
    queue->rear = 0;
    queue->length = 0;

    return true;
}

隊列長度-length接口

由于在順序隊列結(jié)構(gòu)體中沥潭,我們使用 length 保存當(dāng)前隊列中的元素個數(shù)邀泉,因此求隊列長度的操作反而變得簡單直接了。

唯一需要注意的是叛氨,如果當(dāng)順序隊列指針 queue 為空時呼渣,length 接口返回值為 -1

int length(Psqueue queue)
{
    if (queue == NULL)
    {
        return -1;
    }
    
    return queue->length;
}

銷毀隊列-destroy接口

銷毀隊列需要做與創(chuàng)建隊列相反的事情:

  1. 釋放隊列元素緩沖區(qū)寞埠;
  2. 釋放隊列結(jié)構(gòu)體所在內(nèi)存空間屁置;
bool destroy(Psqueue &queue)
{
    if (queue == NULL)
    {
        return false;
    }
    
    free((queue)->buffer);
    free(queue);
    queue = NULL;

    return true;
}

destroy 接口中(queue 類型為Psqueue引用類型),完成相關(guān)內(nèi)存空間釋放操作后仁连,我們還將 queue 指針置為空蓝角,避免其變成野指針。

打印隊列元素-print接口

最后我們給出打印隊列所有元素的接口饭冬,為了直觀方便使鹅,我們以如下格式顯示隊列元素及隊列狀態(tài):

a b c d e f g h i j k l * * * 
H                       R     

* * * d e f g h i j k l * * * 
      H                 R     

q * * d e f g h i j k l m n p 
  R   H 

其中,H昌抠、R 分別表示隊列的隊頭指針和隊尾指針患朱;*號表示當(dāng)前緩沖區(qū)未存儲隊列元素。

以下是 print 接口操作的實現(xiàn):

bool print(Psqueue queue)
{
    POSITION p;
    char *index = NULL;

    int flag = 0;
    if (queue == NULL)
    {
        return true;
    }
    
    p = 0;

#ifdef DEBUG
    index = (char *) malloc(sizeof(char)*queue->MAXNUM);

    if (index != NULL)
    {
        memset(index, ' ', queue->MAXNUM);
        index[queue->rear] = 'R';
        index[queue->head] = 'H';
    }
#endif

    if (queue->rear < queue->head)
    {
        flag = 1;
    }

    for (p = 0; p < queue->MAXNUM; p++)
    {
        if (flag && p >= queue->rear && p < queue->head)
        {
            printf("* ");
        }
        else if (!flag && (p < queue->head || p >= queue->rear))
        {
            printf("* ");
        }
        else
        {
            printf("%-2c", queue->buffer[p]);
        }
    }

    printf("\n");

#ifdef DEBUG
    if (index != NULL)
    {
        for (p = 0; p < queue->MAXNUM; p++)
        {
            printf("%-2c", index[p]);
        }
    }
    printf("\n");
#endif
    
    if (index != NULL)
    {
        free(index);
        index = NULL;
    }

    return true;
}

測試代碼

完成順序隊列的所有接口后炊苫,我們給出其對應(yīng)的測試文件 main.cpp裁厅。 在該文件中,我們對 create侨艾,enqueue执虹,dequeueisEmpty唠梨,top袋励,printdestroy 等接口進行了直接測試当叭,而其他接口則進行了間接的測試茬故。

#include "sqqueue.h"

int main ()
{
    Psqueue queue;
    ELEMTYPE x;

    int i;
    queue = create(15);

    if (queue != NULL)
    {
        for (i = 0; i < 12; i++)
        {
            enqueue(queue, 'a'+i);
        }
        print(queue);

        for (i = 0; i < 3; i++)
        {
            dequeue(queue);
        }
        print(queue);

        for (i = 0; i <= 3; i++)
        {
            enqueue(queue, '1' + i);
        }
        print(queue);

        while (!isEmpty(queue))
        {
            dequeue(queue);
        }
        print(queue);

        while (!isFull(queue))
        {
            enqueue(queue, 'X');
        }
        print(queue);

        destroy(queue);
        print(queue);
    }

    return 0;
}

對項目進行編譯后,程序運行結(jié)果如下所示:

[localhost@queue xgqin]$g++ -o sqQueueTest main.cpp sqqueue.cpp -DDEBUG -g
[localhost@queue xgqin]$ ./sqQueueTest 
a b c d e f g h i j k l * * * 
H                       R     
* * * d e f g h i j k l * * * 
      H                 R     
4 * * d e f g h i j k l 1 2 3 
  R   H                       
* * * * * * * * * * * * * * * 
  H                           
* X X X X X X X X X X X X X X 
R H   

至此科展,我們完成了自定義順序循環(huán)隊列的定義及接口實現(xiàn)均牢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市才睹,隨后出現(xiàn)的幾起案子徘跪,更是在濱河造成了極大的恐慌甘邀,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垮庐,死亡現(xiàn)場離奇詭異松邪,居然都是意外死亡,警方通過查閱死者的電腦和手機哨查,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門逗抑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寒亥,你說我怎么就攤上這事邮府。” “怎么了溉奕?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵褂傀,是天一觀的道長。 經(jīng)常有香客問我加勤,道長仙辟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任鳄梅,我火速辦了婚禮叠国,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘戴尸。我一直安慰自己粟焊,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布孙蒙。 她就那樣靜靜地躺著吆玖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪马篮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天怜奖,我揣著相機與錄音浑测,去河邊找鬼。 笑死歪玲,一個胖子當(dāng)著我的面吹牛迁央,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滥崩,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼岖圈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了钙皮?” 一聲冷哼從身側(cè)響起蜂科,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤顽决,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后导匣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體才菠,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年贡定,在試婚紗的時候發(fā)現(xiàn)自己被綠了赋访。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡缓待,死狀恐怖蚓耽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旋炒,我是刑警寧澤步悠,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站国葬,受9級特大地震影響贤徒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汇四,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一接奈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧通孽,春花似錦序宦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至行剂,卻和暖如春秕噪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背厚宰。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工腌巾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铲觉。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓澈蝙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撵幽。 傳聞我的和親對象是個殘疾皇子灯荧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容