队列是一种数据结构,是用来存储数据的,普通队列是队列里最简单的,也比较好理解。操作和栈是差不多的。队列和栈的区别就是,栈是先进后出,队列是先进先出。然后就没有多大的区别了。
队列的基本概念就这么多,想深入了解概念的请看课本去。这里是使用编程语言来实现队列的。
#include
#include
#include
#define MAXSIZE 10
主要是包含是头文件,然后是预定义一个最大容量。
要实现一个数据结构首先是知道数据结构的样子是什么,然后画出来,然后对应的内容就使用对应的数据类型封装,在C语言中,就使用结构体来封装队列的各个组件。
从图中可以看到的是队列有队头和队尾,所以使用两个标记去表示,left靠队头这边,right靠队尾这边。那就开始封装吧。
typedef struct Queue
{
int elem[MAXSIZE];
int left;
int right;
int curSize;
}Queue,*pQueue;
typedef是取别名:
typedef struct Queue Queue //Queue是struct Queue的别名
typedef struct Queue* pQueue //pQueue是struct Queue*的别名
pQueue creatQueue()
{
pQueue head = (pQueue)malloc(sizeof(Queue));
assert(head);
head->left = -1;
head->right = -1;
head->curSize = 0;
return head;
}
left和right一开始都初始化为-1方便等下操控elem数组。这里的初始化很随意,不过就是等下实现源代码的时候会做一些改变。问题不是很大。在堆区申请的内存,在函数结束后不会释放。
int empty(pQueue head)
{
if (head->curSize == 0)
{
head->left = -1;
head->right = -1;
return 1;
}
return 0;
}
这里之前有个BUG,重新去实现C++STL中的queue才发现的。现在已经改过来了。
之前empty的代码我写成的是下面的样子:
int empty(pQueue head)
{
return head->curSize==0;
}
之所以说它有BUG是因为在后面的pop函数实现的时候,pop函数里面要判断curSize成员是否为0,为0就将left和right赋值为-1(就是回到最初的状态)。但是到了测试下面的方式打印的时候,会有问题。
while(!empty(head))
{
printf("%d ",front(head));
pop(head);
}
问题就是:第一次打印没有任何问题,但是下一次再使用上面的代码打印,就会打印出垃圾值,如果是满队了打印还会报堆溢出的错。
原因就是在while判断的时候empty为真了,所以就不会进入循环,所以在pop函数将left和right赋值为-1的操作就没有得进行。
解决的方法有两个:
一、在while循环结束后再后面再调用一次pop函数。显然这种方式对使用者就有点难受了。也不是我们希望的。
二、把left和right赋值为-1(回到最初的状态)的操作交给empty完成。然后pop也做稍微的修改就可以了。第二种方法才是符号使用者的要求。
int full(pQueue head)
{
return head->curSize >= MAXSIZE; //MAXSIZE是已经预定义的量,10
}
直接判断curSize是否等于队列的最大容量,这就是万金油curSize的好处,如果没有curSize成员的话也可以采用right-left的方式表示当前队列的当前大小(当前队列大小和队列的最大容量是不一样的),但是有curSize成员的话写起来就比较方便一点,省了好多事。因为我不想折磨自己。
int size(pQueue head)
{
return head->curSize;
}
直接返回curSize成员,没什么好说的。
int front(pQueue head)
{
return head->elem[head->left];
}
因为队列的特点是先进先出。所以封装一个获取头部元素的函数来调用队列中的数组的第一个元素(根据实际情况而言,因为等下出队的时候left会往右移动,所以就是返回elem数组中left表示下标下的元素)。函数的结构也很多简单。
void push(pQueue head,int data)
{
if (full(head))
{
printf("队列已满\n");
return;
}
if (empty(head))
{
head->elem[++(head->left)] = data;
head->right = head->left;
head->curSize++;
return;
}
head->elem[++(head->right)] = data;
head->curSize++;
}
先判断当前队列是不是满队,如果满队就结束函数;如果不是满队再判断当前队列是不是空队列,如果空队列就需要同时把left和right标记往右移一位到elem[0]的位置上;如果不是空队列就是正常操作(只用动right)。
可以将上面的代码优化一下,就是让代码更加的简洁:
void push(pQueue head,int data)
{
if (full(head))
return;
if (empty(head))
++head->left;
head->elem[++head->right] = data;
head->curSize++;
}
这样写之后就比原来的简洁很多了。代码量较少,效果也是一样的。(优化之后就不打印“队列已满”这句话了,主要是调试的时候才使用)。
void pop(pQueue head)
{
if(empty(head))
return;
head->left++;
head->curSize--;
}
这里是之前有BUG的时候写的:
根据curSize的值判断当前队列是否为空的队列,如果为空队列就直接结束掉函数,在结束之前记得把left和right复位,回到最初的状态,因为已经出队了left和right没有必要继续待在elem的中间位置或者是最后位置了,而应该是回到elem数组第一个元素的前一个元素,也就是-1的位置。
这里是改了BUG之后的:
因为empty函数中已经把left和right赋值为-1的工作已经做好了,所以在pop函数中就需要判断一下就好,如果是当前队列是空队列就直接结束掉整个pop函数就可以了。如果不是空队列,说明队列中还是有元素的,那就不动尾巴(right)只动头left每调用一次pop函数就把left标记往右移动一位。移动的时候也将curSize做自减运算。
void test2()
{
pQueue que = creatQueue();
for (int i = 0; i < 5; i++)
{
push(que,i);
}
while (!empty(que))
{
printf("%d ", front(que));
pop(que);
}
//pop(que);
printf("\n");
for (int i = 1; i < 11; i++)
{
push(que, i);
}
while (!empty(que))
{
printf("%d ", front(que));
pop(que);
}
//pop(que);
printf("\n");
}
在主函数中调用这个函数,然后就会得到下面的结果:
0 1 2 3 4
1 2 3 4 5 6 7 8 9 10
void test1()
{
pQueue que = creatQueue();
for (int i = 0; i < 3; i++)
{
push(que, i);
}
printf("%d\t%d\n", empty(que), size(que));
while (!empty(que))
{
printf("%d ", front(que));
pop(que);
}
printf("\n%d\t%d\n", empty(que), size(que));
for (int i = 5; i < 9; i++)
{
push(que, i);
}
printf("%d\t%d\n", empty(que), size(que));
while (!empty(que))
{
printf("%d ", front(que));
pop(que);
}
printf("\n%d\t%d\n", empty(que), size(que));
for (int i = 0; i < 6; i++)
{
push(que, i);
}
printf("%d\t%d\n", empty(que), size(que));
for (int i = 6; i < 11; i++)
{
push(que, i);
}
printf("%d\t%d\n", empty(que), size(que));
while (!empty(que))
{
printf("%d ", front(que));
pop(que);
}
printf("\n%d\t%d\n", empty(que), size(que));
for (int i = 10; i < 21; i++)
{
push(que, i);
}
printf("%d\t%d\n", empty(que), size(que));
while (!empty(que))
{
printf("%d ", front(que));
pop(que);
}
printf("\n%d\t%d\n", empty(que), size(que));
}
0 3
0 1 2
1 0
0 4
5 6 7 8
1 0
0 6
0 10
0 1 2 3 4 5 6 7 8 9
1 0
0 10
10 11 12 13 14 15 16 17 18 19
1 0
队列就这样了,反复练就容易了。
把数据结构简单化-----------Cukor丘克