注意上图,它不是循环队列;循环队列的入队和出队见下面,入队出队代码不同,空队满队代码也不同。
由上图可知,循环队列的入队和出队要比非循环队列的入队出队要复杂。
非循环队列:rear总是指向空,front指向第一个元素。
约定循环队列的队头指针指示队头元素在数组中实际位置的前一个位置,队尾指针指示队尾元素在数组中的实际位置。当队尾指针“绕一圈”后赶上队头指针时,视为队满。我个人感觉我还是比较习惯front指向第一个元素,rear指向最后一个元素的下一个位置,这样也是少用一个元素空间解决队满队空条件的问题。具体还是看题目和代码的构造形式吧。
书上原话:可有两种处理方法:其一是另设一个标志位以区别队列是“空”还是“满”;其二是少用一个元素空间,约定以“队列头指针在尾指针的下一位置上作为队列呈“满”状态的标志。
循环队列的结构,因为是用数组,所以指针的变量类型是int
下面是入队和出队的算法,在入队和出队前,分别判断了队列是否满了和是否为空。
队满了吗?:(rear+1)%MAXQSIZE==front
队空了吗?:rear==front
下面是总结
在链队列中,Q.front是头节点,不是首元节点!!
而rear是指向最后一个元素的指针,它的数据域里有值,注意与循环队列和非循环队列(front指向第一个元素,rear指向最后一个元素的后一位)区别。
记住上面两句话,代码不用记也可以慢慢zhou出来。
上图所说的地址的高端,其实就是top和base的起始地址。正常情况下,top和base都是从栈底开始,但是栈底一般都是地址低的那一边,这道题栈底是地址高的那一边,所以栈底地址从4开始,栈顶地址为0。
第三题是因为删除的可能是最后一个节点,此时头尾指针都要修改,而且根据队列实现方式的不同,在删除时可能要修改头指针或者尾指针中的一个。
注意区分堆栈入栈出栈和队列入队出队的区别:
堆栈:首先top和base都在栈底,入栈top位置进入值,然后top++;入栈出栈只有top在移动。
队列:一般情况下,front和rear都在队列位置0的地方,因为队列时先入先出,所以入队是rear在改变,把这想象成排队买票,队头不动,队尾在改变。出队就是队头(front)在移动。
第九题:别忘记分配空间,这里程序默认它没有头节点。
第十题也默认它没有头节点,所以如果遇到模棱两可的题目就把它当作没有头节点,只有首元节点的来做吧。
/* 阅读下面关于循环队列的程序,实现循环队列的入队和出队操作 */
/* 熟悉循环队列的结点类型,掌握循环队列在插入和删除元素在操作上的特点*/
/* 加深对循环队列的理解,逐步培养解决实际问题的编程能力*/
/* 程序填空,运行程序*/
#include
#include
#include
#include
#include
#include
#include
#include
#include /* exit() */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
typedef int Status;
typedef int Boolean;
typedef int QElemType;
/* 队列的顺序存储结构(可用于循环队列和非循环队列) */
#define MAXQSIZE 5
typedef struct {
QElemType *base;
int front;
int rear; //填写语句
} SqQueue;
/* 循环队列的基本操作(9个) */
Status InitQueue(SqQueue *Q) {
(*Q).base = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType)); //填写一条语句
if (!(*Q).base)
exit(OVERFLOW);
(*Q).front = (*Q).rear = 0; //填写一条语句
return OK;
}
Status DestroyQueue(SqQueue *Q) {
if ((*Q).base)
free((*Q).base);
(*Q).base = NULL;
(*Q).front = (*Q).rear = 0;
return OK;
}
Status ClearQueue(SqQueue *Q) {
(*Q).front = (*Q).rear = 0;
return OK;
}
Status QueueEmpty(SqQueue Q) {
if (Q.front == Q.rear) //括号内补充完整
return TRUE;
else
return FALSE;
}
int QueueLength(SqQueue Q) {
return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE; //填写语句
}
Status GetHead(SqQueue Q, QElemType *e) {
if (Q.front == Q.rear)
return ERROR;
*e = *(Q.base + Q.front);
return OK;
}
Status EnQueue(SqQueue *Q, QElemType e) {
if (((*Q).rear + 1) % MAXQSIZE == (*Q).front) //此处补充完整判断条件表达式
return ERROR;
(*Q).base[(*Q).rear] = e;
(*Q).rear = ((*Q).rear + 1) % MAXQSIZE; //填写两条语句
return OK;
}
Status DeQueue(SqQueue *Q, QElemType *e) {
if ((*Q).front == (*Q).rear) //此处补充完整判断条件表达式
return ERROR;
*e = (*Q).base[(*Q).front];
(*Q).front = ((*Q).front + 1) % MAXQSIZE; //填写两条语句
return OK;
}
Status QueueTraverse(SqQueue Q, void(*vi)(QElemType)) {
int i;
i = Q.front;
while (i != Q.rear) {
vi(*(Q.base + i));
i = (i + 1) % MAXQSIZE;
}
printf("\n");
return OK;
}
void visit(QElemType i) {
printf("%d ", i);
}
void main() {
Status j;
int i = 0, l;
QElemType d;
SqQueue Q;
InitQueue(&Q);
printf("初始化队列后,队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
printf("请输入整型队列元素(不超过%d个),-1为提前结束符: ", MAXQSIZE - 1);
do {
scanf("%d", &d);
if (d == -1)
break;
i++;
EnQueue(&Q,
d); //循环体内填写语句,实现依次有4个(MAXQSIZE-1)元素入队,-1作为提前结束符。
} while (iprintf("队列长度为: %d\n", QueueLength(Q));
printf("现在队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
printf("连续%d次由队头删除元素,队尾插入元素:\n", MAXQSIZE);
for (l = 1; l <= MAXQSIZE; l++) {
DeQueue(&Q, &d);
printf("删除的元素是%d,请输入待插入的元素: ", d);
scanf("%d", &d);
EnQueue(&Q, d);
}
l = QueueLength(Q);
printf("现在队列中的元素为: \n");
QueueTraverse(Q, visit);
printf("共向队尾插入了%d个元素\n", i + MAXQSIZE);
if (l - 2 > 0)
printf("现在由队头删除%d个元素:\n", l - 2);
while (QueueLength(Q) > 2) {
DeQueue(&Q, &d);
printf("删除的元素值为%d\n", d);
}
j = GetHead(Q, &d);
if (j)
printf("现在队头元素为: %d\n", d);
ClearQueue(&Q);
printf("清空队列后, 队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
DestroyQueue(&Q);
}