队列
这个队伍在计算机的世界里叫做 队列 ,第一个同学叫做队首,最后一个同学叫做队尾。队首的同学买好饭离开叫做出队,刚来的人加入末尾叫做入队。队列有一个很重要的性质,就是 先进先出 ,First In First Out(FIFO)。
什么叫先进先出呢?通俗的说就是先来的同学一定先打到饭。具体来说就是,每个同学在刚开始加入队伍的时候都必须站在队列的一端(对于打饭的情况来说,就是站在队列的最后一位),而队伍的另一端的同学第一个去打饭;而且在排队过程中不允许两个同学交换顺序。因为有了不允许插队的限制,所以总是先来排队的同学先能够买到饭然后先离开队列,而不会出现后面的同学先买饭离开的情况。
由于队列先进先出的特殊性质,我们在构造它时,需要用两个变量来代表队首和队尾的位置,设置这两个变量有利于我们去维护队列的次序性。在构造函数中,我们会将队首标记置为 0,将队尾标记置为?1,并给队列分配内存空间。而在析构函数中,我们只要把分配给队列的数组空间释放。 接下来我们来学习队列的插入操作。我们在构造队列时,定义了一个队尾标记,在执行入队操作时,只需一直更新队尾标记就能保持好队列元素间的先后关系。
队列插入操作的实现方法如下:
1. 判断队列是否已满。实际上是由于队尾标记不断增加,需要判断队尾标记是否大于数组长度。
2. 更新队尾标记,将新插入元素存入队尾。
如:当前有一个包含元素 1、 2、 3 的队列,此时队尾标记为 2。我们要往队列中插入一个元素 4。 入队时,根据先进先出的性质我们会将新元素放在队尾。 将队尾标记加 1,接着存入新元素就完成了整个入队操作。此时队尾标记为 3。 队列在遍历时也是依靠队首和队尾标记,我们只需把从队首标记上到队尾标记上的元素依次输出就好了。
队列遍历操作的实现方法如下:
1. 输出队首标记所在的元素。
2. 队首标记后移一位。
3. 若队尾标记和队首标记相等,输出最后一个元素,否则返回步骤 1。
队列入队是通过更新队尾标记实现的,那么出队操作又该怎么做呢?
队列出队操作的实现方法如下:
1. 比较队尾标记和队首标记的大小,当队首标记大于队尾标记则说明队列为空了,此时出队操作是非法的。
2. 令队首标记后移一位,队首标记后移即视作原队首出队了。
还是利用刚刚的队列来演示。此时队列中有 1、2、3、 4 四个元素。 当前队首元素为 1,队首标记为 0。 将队首标记后移一位就移除了队首元素。此时队首元素为 2,队首标记为 1。 这样就完成了整个出队过程。
创建队列
#include <stdio.h>
#include <stdlib.h>
typedef struct Queue {
int *data;
int head, tail,length;
}Queue;
void init(Queue *q, int length) {
q->data = (int *)malloc(sizeof(int) *length);
q->lenght = length;
q->head = 0;
q->tail = -1;
}
void clear(Queue *q) {
free(q->data);
free(q);
}
int main() {
Queue *q = (Queue *)malloc(sizeof(Queue));
init(queue, 100);
clear(queue);
return 0;
}
加入队列
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
typedef struct Queeu {
int *data;
int head, tail, length;
}Queue;
void init(Queue *q, int length) {
q->data = (int *)malloc(sizeof(int) * length);
q->length = length;
q->head = 0;
q->tail = -1;
}
int push(Queue *q, int element) {
if(q->tail+1 >= q->length) {
return ERROR;
}
q->tail++;
q->data[q->tail] = element;
return OK;
}
void clear(Queue *q) {
free(q->data);
free(q);
}
int main() {
Queue *queue = (Queue *)malloc(sizeof(Queue));
init(queue, 100);
for(int i = 1; i <=10; i++) {
push(queue, i);
}
clear(queue);
return 0;
}
队列遍历
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
typedef struct Queue {
int *data;
int head, tail, length;
}Queue;
void init(Queue *q, int length) {
q->data = (int *)malloc(sizeof(int) * length);
q->length = length;
q->head = 0;
q->tail = -1;
}
int push(Queue *q, int element) {
if(q->tail + 1 >= q->length) {
return ERROR;
}
q->tail++;
q->data[q->tail] = element;
return OK;
}
void output(Queue *q) {
for(int i = q->head; i <= q->tail; i++) {
printf("%d ",q->data[i]);
}
printf("\n");
}
void clear(Queue *q) {
free(q->data);
free(q);
}
int main() {
Queue *queue = (Queue *)malloc(sizeof(Queue));
init(queue, 100);
for (int i = 1; i <= 10; i++) {
push(queue, i);
}
output(queue);
clear(queue);
return 0;
}
队列中谁是第一名
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
typedef struct Queue {
int *data;
int head, tail, length;
}Queue;
void init(Queue *q, int length) {
q->data = (int *)malloc(sizeof(int) * length);
q->length = length;
q->head = 0;
q->tail = -1;
}
int push(Queue *q, int element) {
if(q->tail + 1 >= q->length) {
return ERROR;
}
q->tail++;
q->data[q->tail] = element;
return OK;
}
void output(Queue *q) {
for (int i = q->head; i <= q->tail; i++) {
printf("%d ", q->data[i]);
}
printf("\n");
}
定义一个返回值为 int 类型,只有一个Queue 类型的指针参数q 的函数 front ,
int front(Queue *q) {
return q->data[q->head];
}
void pop(Queue *q) {
q->head++;
}
int empty(Queue *q) {
return q->head > q->tail;
}
void clear(Queue *q) {
free(q->data);
free(q);
}
int main() {
Queue *queue = (Queue *)malloc(sizeof(Queue));
init(queue, 100);
for (int i = 1; i <= 10; i++) {
push(queue, i);
}
output(queue);
if(!empty(queue)) {
printf("%d\n", front(queue));
pop(queue);
}
output(queue);
clear(queue);
return 0;
}
循环队列
假上溢 。 什么叫“假上溢”呢?回忆一下之前的插入队列的代码:
tail++;
data[tail] = element;
}
当tail达到队列的上限后就不能再插入了,此时再插入就意味着溢出。但是tail达到上限后就意味着要插入的元素真的“无处可放”了么?
我们再来回忆一下删除队首元素的操作。 我们一起来看一遍代码:
head++;
如果一个队列在不断的执行插入、弹出、插入、弹出…那么可以想象,当执行到tail达到队列上限之后,便不能再插入到队列中了,而此时队列其实是空的。
我们该如何解决这个问题呢? 接下来我们会介绍一种目前使用最多的方法:循环队列。
循环队列,顾名思义,就是以循环的方式来存储队列。当队尾标记tail到达队列上限后,如果队列内的元素没有达到上限,就跳转到数组的开始位置,也就是 0 的位置,队首标记到达队列上限也采取同样的处理。通过这样的方法,我们就能够最大化利用内存空间,避免“假上溢”的情况出现。
循环队列的入队和遍历
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
typedef struct Queue {
int *data;
int head, tail, length, count;
} Queue;
请在 init 初始化函数最后一行 写出合适的 对 count 初始化的代码。
void init(Queue *q, int length) {
q->data = (int *)malloc(sizeof(int) * length);
q->length = length;
q->head = 0;
q->tail = -1;
q->count = 0;
}
int push(Queue *q, int element) {
if(q->count >= q->length) {
return ERROR;
}
q->tail = (q->tail + 1) % q->length;
q->data[q->tail] = element;
q->count ++;
return OK;
}
答案应该是 (q->tail + 1) % q->length , 别忘记对 q->length 进行取余. 这里我们先把 do-while 的框架写好。
void output(Queue *q) {
int i = q->head;
do {
printf("%d ", q->data[i]);
i = (i + 1) % q->length;
} while(i != (q ->tail + 1) % q->length);
printf("\n");
}
void clear(Queue *q) {
free(q->data);
free(q);
}
int main() {
Queue *queue = (Queue *)malloc(sizeof(Queue));
init(queue, 100);
for (int i = 1; i <= 10; i++) {
push(queue, i);
}
output(queue);
clear(queue);
return 0;
}
循环队列的出队操作
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
typedef struct Queue {
int *data;
int head, tail, length, count;
}Queue;
void init(Queue *q, int length) {
q->data = (int *)malloc(sizeof(int) * length);
q->length = length;
q->head = 0;
q->tail = -1;
q->count = 0;
}
int push(Queue *q, int element) {
if (q->count >= q->length) {
return ERROR;
}
q->tail = (q->tail + 1) % q->length;
q->data[q->tail] = element;
q->count++;
return OK;
}
void output(Queue *q) {
int i = q->head;
do {
printf("%d ", q->data[i]);
i = (i + 1) % q->length;
} while(i != (q->tail + 1) % q->length);
printf("\n");
}
int front(Queue *q) {
return q->data[q->head];
}
void pop(Queue *q) {
q->head = (q->head + 1) % q->length;
q->count--;
}
int empty(Queue *q) {
return q->count == 0;
}
void clear(Queue *q) {
free(q->data);
free(q);
}
int main() {
Queue *q = (Queue *)malloc(sizeof(Queue));
init(q, 100);
for (int i = 1; i <= 10; i++) {
push(q, i);
}
output(q);
if (!empty(q)) {
printf("%d\n", front(q));
pop(q);
}
output(q);
clear(q);
return 0;
}
|