利用组合实现循环队列
最近在学习李先静老师的那本《系统程序员成长计划》,照着例子写了一个循环队列,利用单向循环链表实现,权当练习。
1. 单向循环链表
我这里设计的只是一个单向循环链表,当然也可以设计为双向链表,效率应该也高点。只提供了需要用到的、最基本的接口给循环队列调用。要保持文头件的简洁,该给的都要给,不该让用户知道的一概不要加进去,否则会造成头文件组织混乱,给用户造成不必要的麻烦。链表的接口如下:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
struct _List;
typedef struct _List List;
List *list_create(DataDestroyFunc destroy_func, void *ctx);
Ret list_append(List *thiz, void *data);
Ret list_delete_tail(List *thiz);
Ret list_get_by_index(List *thiz, size_t index, void **data);
Ret list_foreach(List *thiz, DataVisitFunc visit, void *ctx);
Ret list_destroy(List *thiz);
size_t list_length(List *thiz);
其中,为了封装性,把struct _List的定义放到list.c文件下面,头文件只是给出了声明。
在list.c里面有两个结构题,分别是链表和链表结点:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
typedef struct _listNode
{
struct _listNode *next;
void *data;
} ListNode;
struct _List
{
ListNode *head;
ListNode *tail;
size_t size;
DataDestroyFunc data_destroy_func;
void *data_destroy_ctx;
};
链表结点无须在头文件中声明,因为用户只需要知道如何调用链表的操作,无须了解更多的细节。链表定义了两个指针,分别指向循环链表的开始和结尾,提供了链表的长度size,方便判断链表大小(是否为空);提供了链表结点销毁的函数: data_destroy_func,其中,DataDestroyFunc的声明在typedef.h里,这个头文件主要是定义了一些新的类型和相关的宏定义。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
typedef enum _Ret
{
RET_OK,
RET_INVALID_PARAMS,
RET_FAIL
} Ret;
typedef int (*DataCompare)(void *ctx, void *data);
typedef void (*DataDestroyFunc)(void *ctx, void *data);
typedef void (*DataVisitFunc)(void *ctx, void *data);
#define and &&
#define or ||
#define return_if_fail(p) if (!(p)) \
{printf("%s:%d Warning: "#p" failed.\n",\
__func__, __LINE__); return; }
#define return_val_if_fail(p, ret) if (!(p)) \
{printf("%s:%d Warning: "#p" failed.\n", \
return_if_failed 和 return_val_if_failed 是glibc中判断参数是否有效的方法,只给出warning,然后返回。
接下来,实现链表的接口,相对来简单点。其中,list_foreach是一个遍历链表的函数,第2个参数visit是一个函数指针,第3个参数是保存上下文的参数,这个参数是非常有必要给的,就算目前没用到,也可方便以后扩展。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
static ListNode *list_node_create(void *data)
{
ListNode *thiz = (ListNode *)malloc(sizeof(struct _listNode) );
if (thiz != NULL)
{
thiz->next = NULL;
thiz->data = data;
}
return thiz;
}
List* list_create(DataDestroyFunc destroy_func, void *ctx)
{
List *thiz = (List *)malloc(sizeof(struct _List) );
if (thiz != NULL)
{
thiz->head = NULL;
thiz->tail = NULL;
thiz->size = 0x00;
thiz->data_destroy_func = destroy_func;
thiz->data_destroy_ctx = ctx;
}
return thiz;
}
Ret list_append(List *thiz, void *data)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
ListNode *newNode = list_node_create(data);
if (newNode == NULL)
{
return RET_FAIL;
}
if (thiz->head == NULL)
{
thiz->head = thiz->tail = newNode;
}
else
{
ListNode *cursor = thiz->tail;
cursor->next = newNode;
newNode->next = thiz->head;
thiz->tail = newNode;
}
thiz->size++;
return RET_OK;
}
Ret list_delete_tail(List *thiz)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
ListNode *cursor = thiz->head;
while ( (cursor->next != thiz->tail) and (cursor->next != NULL) )
{
cursor = cursor->next;
}
if (thiz->size > 2)
{
cursor->next = thiz->head;
SAFE_FREE(thiz->tail);
thiz->tail = cursor;
}
else
{
SAFE_FREE(thiz->tail);
thiz->tail = thiz->head;
}
thiz->size--;
return RET_OK;
}
Ret list_get_by_index(List *thiz, size_t index, void **data)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
return_val_if_fail( (index >= 0) and (index < thiz->size), RET_INVALID_PARAMS);
ListNode *cursor = thiz->head;
for (size_t i = 0; i < index; ++i)
{
cursor = cursor->next;
}
*data = cursor->data;
return RET_OK;
}
Ret list_foreach(List *thiz, DataVisitFunc visit, void *ctx)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
return_val_if_fail(visit != NULL, RET_INVALID_PARAMS);
ListNode *cursor = thiz->head;
for (size_t i = 0; i < thiz->size; ++i)
{
visit(ctx, cursor->data);
cursor = cursor->next;
}
return RET_OK;
}
Ret list_destroy(List *thiz)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
ListNode *cursor = thiz->head;
ListNode *prev = NULL;
size_t size = thiz->size;
for (size_t i = 0; i < size; ++i)
{
prev = cursor;
cursor = cursor->next;
thiz->data_destroy_func(thiz->data_destroy_ctx, prev);
thiz->size--;
}
SAFE_FREE(thiz);
return RET_OK;
}
size_t list_length(List *thiz)
{
return_val_if_fail(thiz != NULL, -1);
return thiz->size;
}
实现了单向循环链表之后,可利用链表组合成循环队列了。队列的接口设计为:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
CQueue *cqueue_create(DataDestroyFunc data_destroy_func, void *ctx);
Ret cqueue_head(CQueue *thiz, void **data);
Ret cqueue_push(CQueue *thiz, void *data);
Ret cqueue_pop(CQueue *thiz);
Ret cqueue_foreach(CQueue *thiz, DataVisitFunc visit, void *ctx);
size_t cqueue_length(CQueue *thiz);
Ret cqueue_destroy(CQueue *thiz);
struct _CQueue
{
List *list;
};
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
CQueue *cqueue_create(DataDestroyFunc data_destroy_func, void *ctx)
{
CQueue *thiz = (CQueue *)malloc(sizeof(CQueue) );
if (thiz != NULL)
{
thiz->list = list_create(data_destroy_func, ctx);
if (thiz->list == NULL)
{
SAFE_FREE(thiz);
}
}
return thiz;
}
Ret cqueue_head(CQueue *thiz, void **data)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
return list_get_by_index(thiz->list, 0, data);
}
Ret cqueue_push(CQueue *thiz, void *data)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
return list_append(thiz->list, data);
}
Ret cqueue_pop(CQueue *thiz)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
return list_delete_tail(thiz->list);
}
Ret cqueue_foreach(CQueue *thiz, DataVisitFunc visit, void *ctx)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
return_val_if_fail(visit != NULL, RET_INVALID_PARAMS);
return list_foreach(thiz->list, visit, ctx);
}
size_t cqueue_length(CQueue *thiz)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
return list_length(thiz->list);
}
Ret cqueue_destroy(CQueue *thiz)
{
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
Ret ret = list_destroy(thiz->list);
if (ret != RET_OK)
{
return ret;
}
SAFE_FREE(thiz);
return RET_OK;
}
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#define NUM 50
static void test_all(void)
{
size_t n = NUM;
CQueue *thiz = cqueue_create(data_destroy_func, NULL);
test_append(thiz, n);
printf("\n============ BEGIN OF PRINT ================\n");
cqueue_foreach(thiz, test_print_int, NULL);
printf("\n============= END OF PRINT =================\n\n");
for (size_t i = 0; i < n >> 1; ++i)
{
cqueue_pop(thiz);
}
cqueue_destroy(thiz);
}
END