利用组合实现循环队列

  最近在学习李先静老师的那本《系统程序员成长计划》,照着例子写了一个循环队列,利用单向循环链表实现,权当练习。

1. 单向循环链表

  我这里设计的只是一个单向循环链表,当然也可以设计为双向链表,效率应该也高点。只提供了需要用到的、最基本的接口给循环队列调用。要保持文头件的简洁,该给的都要给,不该让用户知道的一概不要加进去,否则会造成头文件组织混乱,给用户造成不必要的麻烦。链表的接口如下:

代码
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里面有两个结构题,分别是链表和链表结点:

代码
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里,这个头文件主要是定义了一些新的类型和相关的宏定义。

代码
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个参数是保存上下文的参数,这个参数是非常有必要给的,就算目前没用到,也可方便以后扩展。

代码
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;
}

  实现了单向循环链表之后,可利用链表组合成循环队列了。队列的接口设计为:

代码
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;
};
好了,接下来就要组合链表的操作来实现循环队列了,代码很简单:)

代码
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;
}
最后是编写简单的测试代码,验证是否正确:

代码
#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

posted @ 2010-06-19 17:21  Linjian  阅读(541)  评论(0编辑  收藏  举报