0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度解析數(shù)據(jù)結(jié)構(gòu)與算法篇之隊列及環(huán)形隊列的實現(xiàn)

Android編程精選 ? 來源:編程學(xué)習(xí)總站 ? 作者:寫代碼的牛頓 ? 2021-06-18 10:07 ? 次閱讀

01

隊列簡介

隊列是種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),有個元素進(jìn)入隊列稱為入對(enqueue),刪除元素稱為出隊(dequeue),隊列有對頭(head)和對尾(tail),當(dāng)有元素進(jìn)入隊列時就放在對尾的位置。

02

環(huán)形隊列的實現(xiàn)

要想將元素放入隊列我們必須知道對頭和隊尾,在隊列長度不能無限大的條件下我們還要知道隊列的最大容量,我們還想知道隊列大小,所以隊列內(nèi)部能必須記錄當(dāng)前元素數(shù)量。現(xiàn)在我們定義一個結(jié)構(gòu)體如下用于描述隊列。

#define NAN (0xFFFFFE) typedef struct queue_arr{ int head; int tail; int cap; int size; int *arr; }queue_arr_t;

head是對列頭,tail是對尾,cap記錄隊列的最大容量,size記錄當(dāng)前隊列大小,arr指針保存一塊內(nèi)存的首地址。下面我們定義隊列操作函數(shù)。

extern void queue_arr_init(queue_arr_t *q, int capacity); //隊列初始化 extern void enqueue_arr(queue_arr_t *q, int data); //入隊 extern int dequeue_arr(queue_arr_t *q); //出隊 extern void queue_arr_destroy(queue_arr_t *q); //銷毀隊列 extern bool queue_arr_is_full(queue_arr_t *q); //判斷隊列是否滿 extern bool queue_arr_is_empty(queue_arr_t *q); //判斷隊列是否為空 extern int queue_arr_length(queue_arr_t *q); //獲取隊列長度

隊列初始化

void queue_arr_init(queue_arr_t *q, int capacity){ if(q == NULL || capacity 《= 0){ return; } q-》head = 0; q-》tail = 0; q-》size = 0; q-》arr = malloc(capacity * sizeof(int)); q-》cap = capacity; }

入隊

void enqueue_arr(queue_arr_t *q, int data){ if(q == NULL){ return; } if(queue_arr_is_full(q)){ return; } //循環(huán)重用使用過的內(nèi)存 if(q-》tail 》= q-》cap){ q-》tail = 0; } q-》arr[q-》tail++] = data; q-》size++; }

隊列容量有限,在進(jìn)行入隊前一定對隊列進(jìn)行判斷是否已滿。

出隊

int dequeue_arr(queue_arr_t *q){ if(q == NULL){ return NAN; } if(queue_arr_is_empty(q)){ return NAN; } if(q-》head 》= q-》cap){ q-》head = 0; } q-》size--; return q-》arr[q-》head++]; }

出隊時一定要對隊列進(jìn)行判斷是否已空。

判斷隊列是否已滿

bool queue_arr_is_full(queue_arr_t *q){ if(q == NULL){ return false; } return q-》size == q-》cap ? true : false; }

判斷隊列是否已空

bool queue_arr_is_empty(queue_arr_t *q){ if(q == NULL){ return true; } return q-》size == 0 ? true : false; }

獲取隊列長度

int queue_arr_length(queue_arr_t *q){ if(q == NULL){ return 0; } return q-》size; }

銷毀隊列

void queue_arr_destroy(queue_arr_t *q){ if(q == NULL){ return; } if(q-》arr){ free(q-》arr); } q-》arr = NULL; q-》tail = 0; q-》head = 0; q-》cap = 0; q-》size = 0; }

03

鏈?zhǔn)疥犃械膶崿F(xiàn)

為了避免隊列元素的移動我們實現(xiàn)了環(huán)形隊列,但是通過申請一塊內(nèi)存空間實現(xiàn)隊列在隊列大小未知的場景下無法滿足我們不斷加入元素進(jìn)入隊列的需求,這個時候就需要一種無需知道隊列的最大容量,且能動態(tài)插入數(shù)據(jù)和取輸出的隊列實現(xiàn)。

我們知道鏈表能滿足這樣的需求,那么我們可以采用鏈表的實現(xiàn)方式實現(xiàn)隊列。下面我們分別定義一個結(jié)構(gòu)體用于描述鏈?zhǔn)疥犃泻完犃薪Y(jié)點并聲明隊列操作函數(shù)。

typedef struct node{ int data; struct node *next; }node_t; typedef struct queue{ node_t *head; node_t *tail; }queue_t; extern void queue_init(queue_t *q); //隊列初始化 extern void enqueue(queue_t *q, int data); //數(shù)據(jù)入隊 extern int dequeue(queue_t *q); //數(shù)據(jù)出隊列 extern int queue_length(queue_t *q); //獲取隊列長度 extern void queue_destroy(queue_t *q); //銷毀隊列

隊列結(jié)點的next指針像單鏈表一樣指向下一個結(jié)點,我們重點關(guān)注queue_t的定義,head是隊列頭,tail是對尾,有了對頭和對尾就能快速的從對尾插入數(shù)據(jù)從對頭取出數(shù)據(jù)。

隊列初始化

void queue_init(queue_t *q){ if(q == NULL){ return; } q-》head = NULL; q-》tail = NULL; }

入隊

元素每次進(jìn)入隊列都需要新建一個結(jié)點,為了方便我們定義一個新建結(jié)點函數(shù)new_node,函數(shù)實現(xiàn)如下:

static node_t *new_node(int data){ node_t *node = (node_t *)malloc(sizeof(node_t)); if(node == NULL){ return NULL; } node-》next = NULL; node-》data = data; return node; }

入隊函數(shù)實現(xiàn)如下:

void enqueue(queue_t *q, int data){ if(q == NULL){ return; } node_t *node = new_node(data); //新建一個結(jié)點 if(node == NULL){ return; } if(q-》head == NULL && q-》tail == NULL){ //首次插入一個結(jié)點 q-》tail = node; q-》head = node; return; } q-》tail-》next = node; q-》tail = node; }

入隊前要進(jìn)行判斷隊列里是否有結(jié)點,這里通過隊頭和對尾是否為空指針進(jìn)行判斷,如果隊列里沒有結(jié)點則直接將對頭和隊尾指向插入的結(jié)點,否則結(jié)點則通過隊尾tail獲取到隊列最后的結(jié)點,并將最后結(jié)點的next指針指向新插入的結(jié)點,最后更新隊尾。

出隊

每次從隊列移除一個元素都需要釋放隊列結(jié)點內(nèi)存,為了方便我們定義一個是否結(jié)點內(nèi)存函數(shù),函數(shù)實現(xiàn)如下:

static void free_node(node_t *node){ if(node == NULL){ return; } free(node); node-》next = NULL; }

出隊函數(shù)實現(xiàn)如下:

int dequeue(queue_t *q){ if(q == NULL){ return NAN; } if(q-》head == NULL && q-》tail == NULL){ return NAN; } node_t *node = q-》head; int data = node-》data; if(q-》head-》next == q-》tail){ //只有一個結(jié)點 q-》head = NULL; q-》tail = NULL; }else{ //不止一個結(jié)點 q-》head = q-》head-》next; } node-》next = NULL; free_node(node); return data; }

出隊同樣要判斷隊列里是否有結(jié)點,沒有結(jié)點則返回一個非法值,否則進(jìn)行判斷是否只有一個結(jié)點,若只有一個結(jié)點則直接返回頭結(jié)點,并將對頭和隊尾指針置為空指針,否則獲取隊列頭結(jié)點并更新對頭指針。

獲取隊列長度

int queue_length(queue_t *q){ if(q == NULL){ return 0; } node_t *node = q-》head; if(node == NULL){ return 0; } int length = 1; while(node != q-》tail){ node = node-》next; length++; } return length; }

獲取隊列長度只要從隊列頭結(jié)點不斷的遍歷隊列,判斷當(dāng)前結(jié)點是否已到隊列尾部,最后返回隊列長度。

銷毀隊列

void queue_destroy(queue_t *q){ if(q == NULL){ return; } node_t *node = q-》head; if(node == NULL){ return; } node_t *temp = NULL; while(node-》next != q-》tail){ temp = node; node = node-》next; free_node(temp); temp = NULL; } free_node(node); q-》tail = NULL; q-》head = NULL; }

04

結(jié)果驗證

下面我們寫一個小程序驗證隊列實現(xiàn)是否正確。

#include 《stdio.h》 #include “queue.h” int main() { queue_t queue; int i = 0; queue_init(&queue); //隊列初始化 printf(“入隊順序 ”); for(i = 0; i 《 5; i++){ printf(“%d ,”, i + 1); enqueue(&queue, i + 1); } printf(“ ”); printf(“隊列長度: %d ”, queue_length(&queue)); printf(“取出隊列一個數(shù)據(jù):%d ”, dequeue(&queue)); printf(“取出隊列一個數(shù)據(jù):%d ”, dequeue(&queue)); printf(“隊列長度: %d ”, queue_length(&queue)); printf(“銷毀隊列 ”); queue_destroy(&queue); printf(“ ”); printf(“大小固定的隊列測試 ”); queue_arr_t queue2; queue_arr_init(&queue2, 6); printf(“入隊順序 ”); for(i = 10; i 《 16; i++){ printf(“%d ,”, i); enqueue_arr(&queue2, i); } printf(“ ”); if(queue_arr_is_full(&queue2)){ printf(“隊列已滿 ”); }else{ printf(“隊列未滿 ”); } printf(“取出隊列一個數(shù)據(jù):%d ”, dequeue_arr(&queue2)); printf(“取出隊列一個數(shù)據(jù):%d ”, dequeue_arr(&queue2)); if(queue_arr_is_full(&queue2)){ printf(“隊列已滿 ”); }else{ printf(“隊列未滿 ”); } printf(“隊列長度是:%d ”, queue_arr_length(&queue2)); printf(“銷毀隊列 ”); queue_arr_destroy(&queue2); if(queue_arr_is_empty(&queue2)){ printf(“隊列已空 ”); }else{ printf(“隊列未空 ”); } return 0; }

編譯輸出如下:

入隊順序 1 ,2 ,3 ,4 ,5 , 隊列長度: 5 取出隊列一個數(shù)據(jù):1 取出隊列一個數(shù)據(jù):2 隊列長度: 3 銷毀隊列 大小固定的隊列測試 入隊順序 10 ,11 ,12 ,13 ,14 ,15 , 隊列已滿 取出隊列一個數(shù)據(jù):10 取出隊列一個數(shù)據(jù):11 隊列未滿 隊列長度是:4 銷毀隊列 隊列已空

輸出完全正確。

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    6715

    瀏覽量

    88311
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4237

    瀏覽量

    61969

原文標(biāo)題:數(shù)據(jù)結(jié)構(gòu)與算法篇-隊列

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    嵌入式環(huán)形隊列與消息隊列實現(xiàn)原理

    嵌入式環(huán)形隊列,也稱為環(huán)形緩沖區(qū)或循環(huán)隊列,是一種先進(jìn)先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),用于在固定大小的存儲區(qū)域中高效地存儲和訪問
    的頭像 發(fā)表于 09-02 15:29 ?144次閱讀

    玩轉(zhuǎn)RT-Thread消息隊列的應(yīng)用

    不同來源的數(shù)據(jù),確保系統(tǒng)的穩(wěn)定性和響應(yīng)速度。一、設(shè)計消息結(jié)構(gòu)二、創(chuàng)建消息隊列在service.c文件中,我們需要創(chuàng)建一個消息隊列來存放這些消息,并在處理線程中接收和
    的頭像 發(fā)表于 07-23 08:11 ?325次閱讀
    玩轉(zhuǎn)RT-Thread<b class='flag-5'>之</b>消息<b class='flag-5'>隊列</b>的應(yīng)用

    MCU專屬隊列功能模塊QueueForMcu應(yīng)用

    當(dāng)需要從隊列頭部獲取多個數(shù)據(jù),但又不希望數(shù)據(jù)隊列中刪除時,可以使用 Queue_Peek_Array 函數(shù)來實現(xiàn),該函數(shù)的參數(shù)與返回值與
    發(fā)表于 03-20 11:44 ?312次閱讀
    MCU專屬<b class='flag-5'>隊列</b>功能模塊<b class='flag-5'>之</b>QueueForMcu應(yīng)用

    裸機(jī)中環(huán)形隊列與RTOS中消息隊列有何區(qū)別呢?

    環(huán)形隊列”和“消息隊列”在嵌入式領(lǐng)域有應(yīng)用非常廣泛,相信有經(jīng)驗的嵌入式軟件工程師對它們都不陌生。
    的頭像 發(fā)表于 01-26 09:38 ?577次閱讀
    裸機(jī)中<b class='flag-5'>環(huán)形</b><b class='flag-5'>隊列</b>與RTOS中消息<b class='flag-5'>隊列</b>有何區(qū)別呢?

    labview 隊列最前端插入的應(yīng)用

    起到很多作用。本文將詳細(xì)介紹LabVIEW隊列的應(yīng)用,特別是在最前端插入數(shù)據(jù)的情況下。 首先,讓我們了解LabVIEW隊列的基本概念。隊列是一種數(shù)據(jù)
    的頭像 發(fā)表于 01-08 11:45 ?970次閱讀

    labview隊列有什么實際作用

    LabVIEW隊列是一種數(shù)據(jù)結(jié)構(gòu),常用于解決多任務(wù)并發(fā)處理的問題。它被廣泛應(yīng)用于科學(xué)研究、工程項目和自動化控制等領(lǐng)域。在LabVIEW中,隊列提供了一種高效、方便的方式來處理不同任務(wù)之間的數(shù)據(jù)
    的頭像 發(fā)表于 01-05 16:42 ?1246次閱讀

    C語言數(shù)據(jù)結(jié)構(gòu)跳表詳解

    大家好,今天分享一C語言數(shù)據(jù)結(jié)構(gòu)相關(guān)的文章--跳表。
    的頭像 發(fā)表于 12-29 09:32 ?719次閱讀
    C語言<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b><b class='flag-5'>之</b>跳表詳解

    redis數(shù)據(jù)結(jié)構(gòu)的底層實現(xiàn)

    Redis是一種內(nèi)存鍵值數(shù)據(jù)庫,常用于緩存、消息隊列、實時數(shù)據(jù)分析等場景。它的高性能得益于其精心設(shè)計的數(shù)據(jù)結(jié)構(gòu)和底層實現(xiàn)。本文將詳細(xì)介紹Re
    的頭像 發(fā)表于 12-05 10:14 ?517次閱讀

    無鎖隊列解決的問題

    為什么需要無鎖隊列 無鎖隊列解決了什么問題?無鎖隊列解決了鎖引起的問題。 cache失效 當(dāng)CPU要訪問主存的時候,這些數(shù)據(jù)首先要被copy到cache中,因為這些
    的頭像 發(fā)表于 11-10 15:33 ?743次閱讀
    無鎖<b class='flag-5'>隊列</b>解決的問題

    C++環(huán)形緩沖區(qū)設(shè)計與實現(xiàn)

    Buffer) 環(huán)形緩沖區(qū)(Circular Buffer),也被稱為循環(huán)緩沖區(qū)(Cyclic Buffer)或者環(huán)形隊列(Ring Buffer),是一種數(shù)據(jù)結(jié)構(gòu)類型,它在內(nèi)存中形
    的頭像 發(fā)表于 11-09 11:21 ?1272次閱讀
    C++<b class='flag-5'>環(huán)形</b>緩沖區(qū)設(shè)計與<b class='flag-5'>實現(xiàn)</b>

    基于STM32F407的FreeRTOS學(xué)習(xí)筆記(5)

    數(shù)據(jù)結(jié)構(gòu)中有一種很重要的數(shù)據(jù)結(jié)構(gòu)叫做隊列,其特點是數(shù)據(jù)先進(jìn)先出。在FreeRTOS中也有一類隊列,我們利用這類
    的頭像 發(fā)表于 11-07 11:43 ?616次閱讀
    基于STM32F407的FreeRTOS學(xué)習(xí)筆記(5)

    消息隊列的發(fā)展歷史

    上一我們用一個秒殺案例探討了我們?yōu)槭裁葱枰?b class='flag-5'>隊列。今天我們來回顧一下消息隊列的發(fā)展歷史。
    的頭像 發(fā)表于 10-30 10:49 ?866次閱讀
    消息<b class='flag-5'>隊列</b>的發(fā)展歷史

    單片機(jī)裸機(jī)實現(xiàn)隊列功能的方案

    單片機(jī)裸機(jī)實現(xiàn)隊列功能的方案
    的頭像 發(fā)表于 10-17 14:34 ?469次閱讀

    兩個棧實現(xiàn)一個隊列方法

    棧和隊列是比較基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)。無論在工作中,還是在面試中,棧和隊列都用的比較多。在計算機(jī)的世界,你會看到隊列和棧,無處不在。 棧:一個先進(jìn)后出的數(shù)據(jù)
    的頭像 發(fā)表于 10-08 15:54 ?716次閱讀

    延遲隊列實現(xiàn)方式

    延遲任務(wù) 最近有一個需求,基于消息隊列數(shù)據(jù)消費,并根據(jù)多次消費的結(jié)果對數(shù)據(jù)進(jìn)行重新組裝,如果在指定時間內(nèi),需要的數(shù)據(jù)全部到達(dá),則進(jìn)行數(shù)據(jù)
    的頭像 發(fā)表于 09-30 11:17 ?735次閱讀