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

静态队列的大概原理和部分算法讲解已经在上一篇博文中讲过了..

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

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


输出:




,


````````

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nvd11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值