C和指针学习总结之静态数组实现队列

24 篇文章 2 订阅
8 篇文章 0 订阅

介绍

队列是一种先进先出(First-IN First-OUT,FIFO)的结构。排队就是一种典型的队列。首先轮到的是排在队伍最前面的人,新入队的人总是排在队伍的最后。

队列接口——queue.h

/*
 * Interface to a queue module
 */

#include <stdlib.h>
#define QUEUE_TYPE  int /* The type of queue element */

/*
 * insert
 * Add a new element to the queue, and the parameter is the element that needs to be added.
 */
void insert(QUEUE_TYPE value);

/*
 * delete
 * Remove an element from the queue and discard it.
 */
void delete(void);

/*
 * first
 * Returns the value of the first element in the queue, without modifying the queue itseif.
 */
QUEUE_TYPE first(void);

/*
 * is_empty
 * Returns TRUE if the queue is empty, FALSE otherwise.
 */
int is_empty(void);

/*
 * is_full
 * Returns TRUE if the queue is full, FALSE otherwise.
 */
int is_full(void);

实现队列

队列的实现需要两个指针,一个指向队头,一个指向队尾。
假设用一个数组实现队列,front是队头,rear是队尾。
在这里插入图片描述
删除三个元素后:
在这里插入图片描述
数组未满,但它尾部已没有空间,无法插入新的元素。
一种解决方法是当一个元素删除后,队列中的其他元素整体前移一个位置,
但是当数组较大时,这种方法复制时所需的开销太大了。

比较好的方法是使用循环数组。
在这里插入图片描述
插入一个元素后:
在这里插入图片描述
当尾部下标移出数组尾部时,把它设置为0即可。如:

rear = (rear + 1) % QUEUE_SIZE;

但是,这种方法也导致了一个新问题。如何判断队列为空或满?假定队列已满:
在这里插入图片描述
此时front和rear的值分别是3和2。
删除4个元素后:
在这里插入图片描述
删除最后一个元素后:
在这里插入图片描述
现在队列为空时,front和rear的值也是3和2,这和队列满时的值相同。所以无法通过比较front和rear来测试队列是否为空。

解决这个问题的一种方法是如果使数组中的一个元素始终保留不用,这样当队列满时front和rear的值便不相同,可以和队列为空时的情况区分开来。
当队列为空时把rear的值设置为比front的值小1。

判断队列是否为空:

(rear + 1) % QUEUE_SIZE == front;

判断队列是否为满:

(rear + 2) % QUEUE_SIZE == front;

接口实现——a_queue.c

/*
 * A queue implemented as a static array. The size of the array can only be adjusted
 * by changing the #define definition and recompiling the module.
 */

#include "queue.h"
#include <stdio.h>
#include <assert.h>

#define QUEUE_SIZE 100 /* The maximum number of elements in the queue */
#define ARRAY_SIZE (QUEUE_SIZE + 1) /* The length of an array */

/*
 * An array to store the elements of the queue and pointers to the head and tail of the queue.
 */
static QUEUE_TYPE queue[ARRAY_SIZE];
static size_t front = 1;
static size_t rear = 0;

把ARRAY_SIZE的值设置为比QUEUE_SIZE的值大1,实际存储的空间为QUEUE_SIZE,剩下的一个空间用于判断队列是否已满。初始化front的值为1,rear的值为0。

/*
 * insert
 */
void insert(QUEUE_TYPE value)
{
    assert(!isfull());
    rear = (rear + 1) % ARRAY_SIZE;
    queue[rear] = value;
}

插入函数判断队列是否已满,已满时终止程序,否则使rear的值移到下一个可用的位置,再把值存放到该位置。

/*
 * delete
 */
void delete(void)
{
    assert(!is_empty());
    front = (front + 1) % ARRAY_SIZE;
}

删除函数判断队列是否为空,为空时终止程序,否则使front后移一个位置,达到删除一个值的效果。

/*
 * first
 */
QUEUE_TYPE first(void)
{
    assert(!is_empty());
    return queue[front];
}

first函数返回队头的值。

/*
 * is_empty
 */
int is_empty(void)
{
    return (rear + 1) % ARRAY_SIZE == front;
}

is_empty函数用于判断队列是否为空。假设QUEUE_SIZE的值为10,ARRAY_SIZE的值为11。最多删除10个值。初始化时rear为10,front为1。此时队列已满。

删除第1个值时,调用删除函数执行assert(!is_empty());然后调用is_empty函数判断队列是否为空,执行return (rear + 1) % ARRAY_SIZE == front;此时(10+1)%11,0!=front,返回删除函数执行front = (front + 1) % ARRAY_SIZE;front=(1+1)%11,此时rear为10,front为2。

当删除第10个值时,调用删除函数执行语句assert(!is_empty());然后调用is_empty函数判断队列是否为空,执行return (rear + 1) % ARRAY_SIZE == front;此时(10+1)%11,0!=front,返回删除函数执行front = (front + 1) % ARRAY_SIZE;front=(10+1)%11,此时rear为10,front为0,把第10个值删除,此时队列为空。

当再次调用删除函数时,执行assert(!is_empty());然后调用is_empty函数判断队列是否为空,执行return (rear + 1) % ARRAY_SIZE == front;此时(10+1)%11,0==front,if_empty函数返回1,返回删除函数终止程序。

/*
 * is_full
 */
int is_full(void)
{
    return (rear + 2) % ARRAY_SIZE == front;
}

is_full函数用于判断队列是否为满。假设QUEUE_SIZE的值为10,ARRAY_SIZE的值为11。最多插入10个值。初始化时rear为0,front为1。此时队列为空。

插入第1个值时,调用插入函数执行assert(!is_full());然后调用is_full函数判断队列是否已满,执行return (rear + 2) % ARRAY_SIZE == front;此时(0+2)%11,2!=front,返回插入函数执行rear = (rear + 1) % ARRAY_SIZE;rear=(0+1)%11,此时rear为1,front为1。

当插入第10个值时,调用插入函数执行语句assert(!is_full());然后调用is_full函数判断队列是否已满,执行return (rear + 2) % ARRAY_SIZE == front;此时(9+2)%11,0!=front,返回插入函数执行rear = (rear + 1) % ARRAY_SIZE;rear=(9+1)%11,此时rear为10,front为1,把第10个值插入,此时队列已满。

当再次调用插入函数时,执行assert(!is_full());然后调用is_full函数判断队列是否已满,执行return (rear + 2) % ARRAY_SIZE == front;此时(10+2)%11,1==front,if_full函数返回1,返回插入函数终止程序。

总结:

程序的难点在于如何区分队列为空和满的情况,可以存放在数组中的元素总是比数组的空间少1。空下来的那个空间用于判断队列是否已满。
判断是否为空时,用front和rear之间是否相差1个元素来实现;
判断是否为满时,用front和rear之间是否相差2个元素来实现。
插入函数调用is_full()来判断是否还有空间可以插入,删除函数调用is_empty()来判断是否还有元素可以删除。队列是从尾部插入元素,头部删除元素的数据结构。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程小老弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值