最近在学习李先静老师的那本《系统程序员成长计划》,照着例子写了一个循环队列,利用单向循环链表实现,权当练习。
1. 单向循环链表
我这里设计的只是一个单向循环链表,当然也可以设计为双向链表,效率应该也高点。只提供了需要用到的、最基本的接口给循环队列调用。要保持文头件的简洁,该给的都要给,不该让用户知道的一概不要加进去,否则会造成头文件组织混乱,给用户造成不必要的麻烦。链表的接口如下:
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里面有两个结构题,分别是链表和链表结点:
{
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里,这个头文件主要是定义了一些新的类型和相关的宏定义。
{
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个参数是保存上下文的参数,这个参数是非常有必要给的,就算目前没用到,也可方便以后扩展。
{
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;
}
实现了单向循环链表之后,可利用链表组合成循环队列了。队列的接口设计为:
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);
{
List * list;
};
{
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;
}
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