一、背景需求
在上一篇文章中介紹了數組實現的隊列,本章節再補充一個鏈式的隊列,實現動態插入空間的操作。
二、相關知識
鏈式隊列由環形鏈表實現,通過鏈表頭和鏈表尾的操作,完成了隊列入列出列的功能。
如圖所示,空隊列時至少需要一個空節點,表示爲隊列頭和隊列尾,相當於哨兵的作用;
入列:新增節點插入到隊列尾,並更新 tail 指針;出列:更新 head 位置,釋放原來 head節點,返回現有位置的 data;
三、實現
相關數據結構
struct list_node_t {
struct list_node_t *next;
void *data;
};
typedef struct queue
{
size_t max_size;
size_t used_num;
struct list_node_t *head;
struct list_node_t *tail;
} queue_t;
初始化函數,對於生成圖1的空隊列,注意初始化時尾節點指向頭節點
queue_t *queue_alloc(size_t max_size)
{
queue_t *pqueue = NULL;
pqueue = calloc(1, sizeof(queue_t));
if ( !pqueue ) {
return NULL;
}
/* Head is nil */
pqueue->head = calloc(1, sizeof(struct list_node_t));
if ( !pqueue->head ) {
return NULL;
}
pqueue->head->next = NULL;
pqueue->head->data = NULL;
/* Reset tail means empty */
pqueue->tail = pqueue->head;
pqueue->max_size = max_size;
pqueue->used_num = 0;
return pqueue;
}
入列操作,對於圖2的步驟,新增節點後加入到隊列尾
int queue_push(queue_t *pqueue, void *nill, void *pdata)
{
struct list_node_t *pnode = NULL;
if ( !pqueue ) {
return FAILURE;
}
if ( queue_isfull(pqueue) ) {
return FAILURE;
}
pnode = calloc(1, sizeof(struct list_node_t));
if ( !pnode ) {
return FAILURE;
}
pnode->data = pdata;
pnode->next = NULL;
pqueue->tail->next = pnode;
pqueue->tail = pnode;
pqueue->used_num++;
return SUCCESS;
}
出列操作,對應圖3步驟,注意data數據爲外部維護的數據結構,隊列內部只做索引維護。
void *queue_pop(queue_t *pqueue)
{
struct list_node_t *prev = NULL;
if ( !pqueue ) {
return NULL;
}
if ( queue_isempty(pqueue) ) {
return NULL;
}
prev = pqueue->head;
pqueue->head = pqueue->head->next;
FREE_POINTER(prev);
return pqueue->head->data;
}
四、總結
相比於數據隊列,鏈式隊列空間使用較少,同樣也可用於單生產者-單消費者的無鎖隊列實現。
注意data數據爲外部維護的數據,隊列內部只是做指針索引管理,並不進行內容的修改。