您的位置:首页 > 理论基础 > 数据结构算法

数据结构 --静态队列的一个简单的C语言代码实现

2013-04-21 23:11 1166 查看
静态队列的大概原理和部分算法讲解已经在上一篇博文中讲过了..
http://blog.csdn.net/nvd11/article/details/8816699
这里接上篇文章, 用c语言来实现1个简单的静态队列容器.'

1. 首先编写1个头文件

国际惯例, 我们会在这个头文件中写如如下内容:

1. 首先是写上静态队列本身的类型.

2. 我们也会把这个容器的公共函数声明到这个头文件中.

那么其他文件只需要引用这个头文件, 就可以使用这个容器类型和公共函数了.

代码如下:

arrqueue.h

#include "bool_me.h"
#ifndef __ARRQUEUE1_H_
#define __ARRQUEUE1_H_
struct person_aq{
int id;
char name[16];
};

typedef struct person_aq PERSON_AQ;

struct aq_person{
PERSON_AQ * pArr;
int Front;
int Rear;
int arrlen;
BOOL is_inited;

int (* len)(); //function member of a structure
BOOL (* is_full)(); //judge whether array queue is full
BOOL (* is_empty)(); //judge whether array queue is empty
BOOL (* extend)(); //extend the max length of the array queue
BOOL (* en_queue)(); //enqueue by id and name
BOOL (* en_queue_by_struct)(); //enqueue by structure
BOOL (* de_queue)(); //dequeue
void (* print)(); //print info of all the elements in the array queue
void (* print_arr_id_st)(); //print all the id of elements of the array
};

typedef struct aq_person AQ_PERSON;

//initial and return an array queue with dynamic memory assigned
AQ_PERSON * aq_person_new(int arrlen);

//free the memory of array queue
BOOL aq_person_free(AQ_PERSON * pAq);

//print an structure object
void person_aq_print(PERSON_AQ * pnode);

#endif /* ARRQUEUE1_H_ */


适当地解析下:'

首先. bool_me.h 头文件只不过是定义了 BOOL 宏的头文件.. 实际上是char类型啦.

1. 结构体 PERSON_AQ 是1个存放数据的结构体类型, 它有两个成员id, name. 因为下面的静态数组实际上是1个静态结构体数组

2. 结构体 AQ_PERSON 是1个静态队列的容器类型, 它里面包含1个动态分配的 PERSON_AQ结构体数组.

成员 PArr 就是该动态数组的头部地址

成员 Front Rear 就是队列的出口元素地址 和 入口地址了.

成员 arrlen 就是动态数组的长度, 实际上也等于静态队列的最大长度 + 1

成员 is_inited 用于判断这个结构体是否被初始化, 否则不能使用啊.

下面还定义了1些必要的函数成员(函数), 这些函数成员在结构体初始化时会被指定指向哪个函数. 其中出列和入列函数都在这些函数成员中了.

3. 最后还定义3个公共函数, 分别是静态队列结构体初始化函数和释放函数, 和打印1个队列中的1个结构体元素函数.

至于每个函数的大概用途我都注释在旁边了, 下面会对这些函数逐个实现.

2. 静态队列初始化函数 ap_person_new

分析下这个函数的逻辑, 我们要在这个函数里做如下事情:

1. 为1个静态队列指针, 根据传入的长度参数动态动态分配1段内存.

2. Front 和 Rear 成员的值都设为0, 表示这个是1个空队列, 并根据参数为arrlen 这个数据长度成员赋值.

3. 为成员函数赋值..

4. 成员is_inited 设为 TRUE, 表示已经初始化过了.

5. 返回这个静态队列指针

代码如下:

//initail and return an array queue with dynamic memory assiganed
AQ_PERSON * aq_person_new(int arrlen){
if (arrlen <= 1){
printf("length of array must > 1!!\n");
return NULL;
}

AQ_PERSON *  pAq;
pAq = (AQ_PERSON *)malloc(sizeof(AQ_PERSON));
if (NULL == pAq){
base_error("fail to assign memory to a new array queue!!");
}

pAq->pArr = (PERSON_AQ *)malloc(sizeof(PERSON_AQ) * arrlen);
if (NULL == pAq->pArr){
base_error("fail to assign memory to a new array queue!!");
}

pAq->Front = 0;
pAq->Rear = 0;
pAq->arrlen = arrlen;

pAq->len = &aq_len;
pAq->is_full = &aq_is_full;
pAq->is_empty = &aq_is_empty;
pAq->extend = &aq_extend;
pAq->en_queue = &aq_enqueue;
pAq->en_queue_by_struct = &aq_enqueue_bystruct;
pAq->de_queue = &aq_dequeue;
pAq->print = &aq_print;
pAq->print_arr_id_st = &aq_print_all_id_status;

pAq->is_inited = TRUE;
return pAq;
}


3. 获取队列长度函数 aq_len

至于怎样根据 Front 和 Rear 位置 和 arrlen数组长度这个3个参数去获取队列的长度?

在上一篇文章里详细解析过了, 这里不在多说了, 只会写下公式.

((Rear - Front) + arrlen) % arrlen

代码如下:

//get the length of array queue
static int aq_len(AQ_PERSON * pAq){
if (TRUE != pAq->is_inited){
base_error("the array queue is not initialed!!");
}

return (pAq->Rear - pAq->Front + pAq->arrlen) % pAq->arrlen;
}


4. 判断队列是否已满函数, qa_is_full

至于怎样判断也请参考上一篇文章, 就是判断 是否 Rear 下1个位置是 Front

当然也可以判断队列的长度是否等于数组的长度(arrlen) -1

代码如下:

//judge whether the array queue is full
static BOOL aq_is_full(AQ_PERSON * pAq){
if (TRUE != pAq->is_inited){
base_error("the array queue is not initialed!!");
}

if ((pAq->Rear +1) % pAq->arrlen == pAq->Front){
return TRUE;
}

return FALSE;
}


5. 判断队列是否空队列函数, qa_is_empty

这个更简单. 判断 Front 和 Rear 是否相等就ok了.

代码如下:

static BOOL aq_is_empty(AQ_PERSON * pAq){
if (TRUE != pAq->is_inited){
base_error("the array queue is not initialed!!");
}

if (pAq->Front == pAq->Rear){
return TRUE;
}

return FALSE;
}


6. 根据 id 和 name 的入列函数 aq_en_queue

就是把 id 和 name的数据作为参数, 新加入1个队列到队列中.

大概逻辑在上一篇文章中提过了, 但是实现起来还是有点复杂的.

大概逻辑步骤:

1. 判断对列是否已满

2. 如果满了, 则对包含这个队列的静态进行扩充(aq_extend), 扩充的长度是当前数组长度的2分1

3. 把 id 和 name写入 Rear位置的 数组结构体成员中.

4. Rear的位置加1

代码如下:

//add an elements to rear of queue by id and name
static BOOL aq_enqueue(AQ_PERSON * pAq, int id, char * pname){
if (TRUE != pAq->is_inited){
base_error("the array queue is not initialed!!");
}

if (TRUE == pAq->is_full(pAq)){
if(FALSE == pAq->extend(pAq, pAq->arrlen/2)){
return FALSE;
}
}

pAq->pArr[pAq->Rear].id = id;
strncpy(pAq->pArr[pAq->Rear].name, pname+0, 16);

pAq->Rear = (pAq->Rear + 1) % pAq->arrlen;
return TRUE;
}


问题来了, 关键是 aq_extend 的函数怎样写? 下面会讲

7. 队列最大长度扩展函数 aq_extend

当队列已经满, 还需要入列函数时, 怎么办? 当然可以提示用户不能再入列, 但是通常我们会实现自动扩容功能.

实际上的原理就是对队列所在的动态数组进行 重新分配内存(realloc).

而用户也不需关心这些信息, 所以这个函数是静态函数, 也不会加入结构体的函数成员中, 所以外部是无法直接调用的.

大概原理也在上一篇文章的最后讲过了, 这里写下步骤:

1. 对动态分配的数组重新分配一段更长的内存.

2. 如果Rear 的位置比 Front 更前...就将Front的位置向后移动x , x是扩充的数量, 这个过程包括数组元素在数组内的移动, 具体请参考上一篇文章啦

代码如下:

//extend the max length of array queue
static BOOL aq_extend(AQ_PERSON * pAq, int exlen){
PERSON_AQ * pold;
pold = pAq->pArr;
pAq->pArr = (PERSON_AQ *)realloc(pAq->pArr, sizeof(PERSON_AQ) * (pAq->arrlen + exlen));
if (NULL == pAq->pArr){
printf("fail to assign memory to extend the array!\n");
pAq->pArr = pold;
return FALSE;
}

//	realloc will free the old memory automatically,
//	it's now allow to free it manual!!!
//	if (pAq->pArr != pold){
//		free(pold);
//	}

if (pAq->Rear < pAq->Front){
int i;
for (i=pAq-> arrlen - pAq->Front; i>0; i--){
pAq->pArr[pAq->Front+i-1+exlen] = pAq->pArr[pAq->Front + i - 1];
}
pAq->Front += exlen;
}

pAq->arrlen += exlen;
return TRUE;
}


8. 根据1个结构体参数的入列函数 aq_queue_by_struct

因为c语言没有函数重载功能, 只能写多1个函数了, 也很简单, 直接调用上面的 入列函数就ok了

static BOOL aq_enqueue_bystruct(AQ_PERSON * pAq, PERSON_AQ * pnode){
return aq_enqueue(pAq, pnode->id, pnode->name);
}


9. 出列函数 aq_dequeue

这个函数的返回值是也是布尔类型, 因为出列是不一定成功的, .

出列的意思就是把出口元素 从队列中删除啦, 实际应用大部分情况下用户还需要获得这个出口元素, 所以这个出列函数还会接受1个 结构体指针, 把出列函数的数据复制到这个指针所指向的结构体中.

步骤:

1. 判断是否空队列, 否则返回FALSE

2. 把出口元素的数据 赋值给 output 参数指针

3. Front 移向下1个位置

代码 如下:

//dequeue
static BOOL aq_dequeue(AQ_PERSON * pAq, PERSON_AQ * pnode){
if (TRUE != pAq->is_inited){
base_error("the array queue is not initialed!!");
}

if (TRUE == pAq->is_empty(pAq)){
printf("the array queue is empty!\n");
return FALSE;
}

pnode->id = pAq->pArr[pAq->Front].id;
strncpy(pnode->name, pAq->pArr[pAq->Front].name+0, 16);
pAq->Front = (pAq->Front + 1) % pAq->arrlen;
return TRUE;
}


10. 打印队列中其中1个元素的函数 person_aq_print

这个函数作用就是输出其中1个元素的信息.

很简单啦, 代码如下

//print an structure object
void person_aq_print(PERSON_AQ * pnode){
printf("id is %d, name is %s\n", pnode->id, pnode->name);
}


11. 打印队列中所有元素函数 aq_print

原理也不复杂, 就是从Front 开始遍历 直到 Rear , 然后调用上面的函数输出就ok了

代码如下:

//print all the elements of array queue
static void aq_print(AQ_PERSON * pAq){
if (TRUE != pAq->is_inited){
base_error("the array queue is not initialed!!");
}

if (TRUE == pAq->is_empty(pAq)){
printf("the array queue is empty!\n");
}

int index;
index = pAq->Front;
while(index != pAq->Rear){
person_aq_print(&(pAq->pArr[index]));
index = (index+1) % pAq->arrlen;
}
}


12. 打印数组中所有元素的id 的信息 aq_print_all_id_status

就是按照数组自然顺序输出数组中所有元素信息, 如果某个元素不在队列中, 则输出'x', 否则输出元素的id.

效果如下:

3, 4, x, x, 5, 6, 7, 1, 2

3, 4, 8, 9, x, x, x, x, 5, 6, 7, 1, 2

作用? 方便我调试啦, 可以直观地知道队列每个元素在数组的位置

代码如下:

//print all the id of element in the array, not only in queue
static void aq_print_all_id_status(AQ_PERSON * pAq){
if (TRUE != pAq->is_inited){
base_error("the array queue is not initialed!!");
}

if (TRUE == pAq->is_empty(pAq)){
printf("the array queue is empty!\n");
return;
}

int  index;
for (index=0; index < pAq->arrlen; index++){
if (TRUE == is_in_queue(pAq, index)){
printf("%d", pAq->pArr[index].id);
}
else{
printf("x");
}

if (index < pAq->arrlen -1){
printf(", ");
}
}

printf("\n");
return;
}


13. 根据下标判断数组中某个元素是否在队列中 is_in_queue

为什么要写这个函数? 因为上面的函数用到啊....

逻辑也很简单:

代码如下:

//judge whether an element of the array is in the queue
static BOOL is_in_queue(AQ_PERSON * pAq, int index){
int Front= pAq->Front;
int Rear = pAq->Rear;
if (Front == Rear){
return FALSE;
}
else if (Front < Rear){
if (index >= Front && index < Rear){
return TRUE;
}
return FALSE;
}
else{ //Front > Rear
if (index < Rear || index >= Front){
return TRUE;
}
return FALSE;
}
}


14. 释放静态队列函数 aq_person_free

因为静态队列容器本身和 里面的数组都是动态分配的, 所以必须手动释放啦

步骤很简单

1.释放静态队列结构体里面的 数组成员

2. 释放结构体本身

代码如下:

//free the memory of array queue
BOOL aq_person_free(AQ_PERSON * pAq){
if (TRUE != pAq->is_inited){
printf("the array queue is not initialed!!\n");
return FALSE;
}

free(pAq->pArr);
free(pAq);
return TRUE;
}


好了, 队列的必要函数我大概都写出来, 因为队列不能中间插入和删除, 也不需排序, 所以函数还是比较少的.

15. 最后写个小测试程序..

代码如下:

int arrqueue1(){
AQ_PERSON * paq1 = aq_person_new(6);
paq1->en_queue(paq1, 1, "Jason");
paq1->en_queue(paq1, 2, "Gateman");
paq1->en_queue(paq1, 3, "Youyi");
paq1->en_queue(paq1, 4, "Snow");
paq1->en_queue(paq1, 5, "Moon");
paq1->en_queue(paq1, 6, "Crystal");
paq1->en_queue(paq1, 7, "AI");
paq1->print(paq1);
paq1->print_arr_id_st(paq1);
printf("\n");

PERSON_AQ node;

paq1->de_queue(paq1, &node);
printf("the dequeue object is:\n");
person_aq_print(&node);
printf("\n");

paq1->en_queue_by_struct(paq1, &node);

paq1->de_queue(paq1, &node);
printf("the dequeue object is:\n");
person_aq_print(&node);
printf("\n");

paq1->en_queue_by_struct(paq1, &node);

paq1->de_queue(paq1, &node);
printf("the dequeue object is:\n");
person_aq_print(&node);
printf("\n");

paq1->en_queue_by_struct(paq1, &node);

paq1->de_queue(paq1, &node);
printf("the dequeue object is:\n");
person_aq_print(&node);
printf("\n");

paq1->en_queue_by_struct(paq1, &node);

paq1->print_arr_id_st(paq1);

paq1->en_queue(paq1, 8, "Cindy");
paq1->en_queue(paq1, 9, "Inzaghi");

paq1->print_arr_id_st(paq1);
paq1->print(paq1);
aq_person_free(paq1);
printf("len of array queue is %d\n",paq1->len(paq1));
printf("arrqueue1_main done\n");
return 0;
}


可以见到我不断地执行入列操作, 其中也有自动扩容的情况啦~

输出:



,

````````
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐