c印記(十二):隊列queue原理與實現

一、簡而言之

在百度百科裏面摘取了一段關於隊列(queue)的介紹:

隊列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。

二、一般而言

這裏是對就一般而言, 隊列的結構,操作方法等的表述。

2.1 結構

以下爲隊列的結構示意圖

隊列結構示意圖

2.2 實現分類

一般來說,隊列有兩類實現方式:

  • 數組實現:暫時將其稱爲有限隊列,根據創建隊列是傳入的size,創建相應size的數組來作爲隊列的載體,一般都會以循環的方式利用數組,也可看作循環隊列。

    • 優點:預先分配好內存,在入隊和出隊過程中不會重新分配內存,有一定時間上的優勢。
    • 缺點:因必須預先分配好內存,所以存在內存空間浪費的問題(比如分配了10個元素,但很多時候卻只有5個有效元素),元素數目固定,不夠靈活。

循環隊列示意圖

  • 鏈表實現:可稱其爲鏈式隊列,使用鏈表來實現隊列,理論上這樣的隊列可以無限延伸,當然根據實際需要,可以人爲的限制隊列長度,讓其表現的和數組實現方式差不多。

    • 優點: 無需預先分配內存,所以不存在空間浪費的問題(雖然會多一個指針域,但也基本可以接受),隊列長度可限制也可不限制,較爲靈活。
    • 缺點: 入隊出隊分別需要分配和釋放元素節點,在時間上會略微慢於數組實現。

總的來說,在可以確定隊列長度最大值的情況下,建議用循環隊列,如果你無法預估隊列的長度時,則用鏈隊列。

2.3 基本操作

這裏主要指的是隊列的入隊(enqueue)與出隊(dequeue),對於鏈式隊列來說,其入隊和出隊也就是鏈表的尾部插入與移除頭部節點,這裏就不多說,主要說一下以數組實現的循環鏈表的具體實現(其中head和tail都是表示數組的下標)。

  • 入隊: 如上面的循環隊列示意圖,入隊操作,需要先判斷tail節點的下一個位置是否是head,如果是就表示隊列已滿,無法入隊,如果不是,那就可以入隊,也即是將元素放入tail的下一個位置,最後將tail加一(也就是指向最終的尾元素),其僞代碼如下:
/** 取餘保證,當tail = size - 1 時,轉回到0 */
int tail = (queue->tail + 1)  % queue->size; 

if (tail != queue->head)   
{ /** 此處這樣寫,是因爲正常情況下,非滿的情況多於滿了的情況,這樣寫可以稍微的加快點執行速度 */
    queue->elems[queue->tail] = elem;
    queue->tail = tail;
}
else  /** 此時隊列已滿 */
{
    printf("the queue is full!");
}
  • 出隊: 先判斷 tail 和head是否相等,如果相等,就比較隊列已空,無元素可出,不等則表示非空。此時需要取出head對於位置的元素,然後將head加1(當然,爲了能夠在head 增加到數組尾部時能夠轉回到首部,那麼這裏其實需要 將head加1與隊列的size取餘再賦值給head),其僞代碼如下:
elem_type elem = elem_null; /** 初始化元素 */
if(queue->tail != queue->head)     /** 判斷隊列不爲空 */
{/**這樣寫的作用與上一段僞代碼的作用是一樣的 */
    elem = queue->elems[queue->head];

    /** 取餘保證,當head= size - 1 時,轉回到0 */
    queue->head = (queue->head+1) % queue->size;
}
else /** 此時隊列已空 */
{
    printf("the queue is empty \n");
}

return elem;

三、和盤托出

這裏就是我的具體實現。目前我實現的是無限隊列(鏈式隊列 ),鏈表就是前一章實現的list。

  • 其頭文件如下:
#ifndef __TINY_QUEUE_H__
#define __TINY_QUEUE_H__


#include "general_type.h"


#ifdef __cplusplus
extern "C" {
#endif

/***************************************************************************
 *
 * macro declaration
 *
 ***************************************************************************/

/***************************************************************************
 *
 * data structure declaration
 *
 ***************************************************************************/

/** the callback function for free item */
typedef void (*tfQueueItemFreeFunc_t)(void* item);
/***************************************************************************
 *
 * API declaration
 *
 ***************************************************************************/

/**
 *@brief  create a queue instance 
 *
 *@param  none
 *
 *@return success: queue instance handle, fail: NULL.
 *@see
 *
 */
G_API GPHD tfQueueCreate(void);

/**
 *@brief  destroy a queue instance 
 *
 *@param  queue [in] pointer to queue instance
 *
 *@return none.
 *@see
 *
 */
G_API void tfQueueDestroy(GPHD queue);

/**
 *@brief  clear(remove and free) all item in queue
 *
 *@param  queue     [in] pointer to queue instance
 *@param  item_free [in] callbck function for free item.
 *
 *@return none.
 *@see
 *
 */
G_API void tfQueueClear(GPHD queue, tfQueueItemFreeFunc_t item_free);

/**
 *@brief  append a item to current queue.
 *
 *@param  queue [in] pointer to queue instance
 *@param  item  [in] pointer new item.
 *
 *@return success: GTRUE, fail: GFALSE
 *@see
 *
 */
G_API GBOL tfQueueEnQueue(GPHD queue, void* item);

/**
 *@brief  pop a item from current queue 
 *
 *@param  queue [in] pointer to queue instance
 *
 *@return success: item handle, fail: NULL.
 *@see
 *
 */
G_API void *tfQueueDeQueue(GPHD queue);

/**
 *@brief  get the count of item in current queue.
 *
 *@param  queue [in] pointer to queue instance
 *
 *@return how many item in current queue.
 *@see
 *
 */
G_API GU32 tfQueueLength(GPHD queue);




#ifdef __cplusplus
}
#endif

#endif //end of __TINY_QUEUE_H__
  • 其源文件如下:
#include <string.h>

#include "general_macro.h"
#include "tiny_queue.h"
#include "tiny_list.h"
#include "vos_mem.h"


/***************************************************************************
 *
 * macro declaration
 *
 ***************************************************************************/
#define LOGE printf
#define LOGD printf
#define LOGW printf
/***************************************************************************
 *
 * data structure declaration
 *
 ***************************************************************************/
/** data structure for item */
typedef struct my_queue_item_s
{
    tiny_list_node_t node;
    void* data;
}my_queue_item_t;

/** data structure for queue */
typedef struct my_queue_s
{
    tiny_list_t items;
}my_queue_t;

/***************************************************************************
 *
 * inner API define
 *
 ***************************************************************************/
static my_queue_item_t* tqItemNew(void* data)
{
    my_queue_item_t* item = (my_queue_item_t*)memAlloc(sizeof(my_queue_item_t));

    if (item)
    {
        item->data = data;
    }

    return item;
}
/***************************************************************************
 *
 * API define
 *
 ***************************************************************************/
GPHD tfQueueCreate(void)
{
    my_queue_t* mq = (my_queue_t*)memAlloc(sizeof(my_queue_t));

    if (mq)
    {
        memset(mq, 0, sizeof(*mq));
        tfListInitialize(&(mq->items), memFree);
    }
    else
    {
        LOGE("alloc queue instance failed\n");
    }

    return mq;
}


void tfQueueDestroy(GPHD queue)
{
    if (queue)
    {
        my_queue_t* mq = (my_queue_t*)queue;
        tiny_list_t* tl = &(mq->items);
        GU32 left_count = tfListCount(tl);

        if (left_count > 0) 
        {
            LOGW("here have: %d items in queue, it's maybe lead to memory leak\n", left_count);
        }

        tfListClear(tl);

        memFree(queue);
    }
}


void tfQueueClear(GPHD queue, tfQueueItemFreeFunc_t item_free)
{
    if (queue)
    {
        my_queue_t* mq = (my_queue_t*)queue;
        tiny_list_t* tl = &(mq->items);
        if (item_free)
        {
            tiny_list_node_t* node = tfListFront(tl);

            while (node)
            {
                my_queue_item_t* mqi = (my_queue_item_t*)node;

                item_free(mqi->data);

                node = node->next;
            }
        }
        else
        {
            LOGW("item_free is NULL, so here wouldn't free item, it maybe lead to memory leak\n");
        }

        tfListClear(tl);
    }
}


GBOL tfQueueEnQueue(GPHD queue, void* item)
{
    GBOL ret = GFALSE;
    if (queue)
    {
        my_queue_t* mq = (my_queue_t*)queue;
        tiny_list_t* tl = &(mq->items);
        my_queue_item_t* mqi = tqItemNew(item);

        GCHECK(mqi);

        tfListPushBack(tl, (tiny_list_node_t*)mqi);
        /** todo, is need check push to list is successed ?? */
        ret = GTRUE;        
    }

    return GFALSE;
}


void *tfQueueDeQueue(GPHD queue)
{
    void* item = NULL;
    if (queue)
    {
        my_queue_t* mq = (my_queue_t*)queue;
        tiny_list_t* tl = &(mq->items);
        my_queue_item_t* node = (my_queue_item_t*)tfListFront(tl);

        if (node)
        {
            item = node->data;
            tfListPopFront(tl); /** remove head node from list */
        }
    }

    return item;
}


GU32 tfQueueLength(GPHD queue)
{
    GU32 ret = 0;
    if (queue)
    {
        my_queue_t* mq = (my_queue_t*)queue;

        ret = tfListCount(&(mq->items));
    }

    return ret;
}

其中的 vos_mem.h是我封裝的虛擬系統接口中關於memory操作的部分,以作跨平臺使用,以上源文件中使用到的memAlloc,memFree可以簡單的理解爲標準的malloc和free。general_macro.h可以去看第九章。

四、捫心自問

這裏只是實現了無限隊列的情況,如果後續在開發的過程中還需要有限隊列的話,再添加相關接口與實現代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章