程序内功篇五--队列
一、队列
1、队列原理
队列
是限制在两端
进行插入操作
和删除操作
的线性表
允许进行
存入操作
的一端称为“队尾
”
允许进行
删除操作
的一端称为“队头
”
当线性表中
没有元素
时,称为“空队
”
特点 :先进先出(
FIFO
)
2、队列演示
二、顺序队列
>1、顺序队列原理
规定:
front指向队头元素的位置; rear指向队尾元素的下一个位置。
在队列操作过程中,为了提高效率,以调整指针代替队列元素的移动,并将
数组
作为循环队列
的操作空间。
为区别空队
和满队
,满队元素个数比数组元素个数少一个。
因此顺序队列
的结构体
定义为:
#define DEBUG 1 //1-打开debug,0关闭debug
typedef int data_t ; /*定义队列中数据元素的数据类型*/
#define N 64 /*定义队列的容量*/
typedef struct {
data_t data[N] ; /*用数组作为队列的储存空间*/
int front, rear ; /*指示队头位置和队尾位置的指针*/
} s_queue,*squeue ; /*顺序队列类型定义*/
>2、顺序队列的创建
/**
* @description: 顺序队列的创建
* @param {*}
* @return {squeue-队列指针}
*/
squeue squeue_create()
{
squeue queue = (squeue)malloc(sizeof(s_queue));
if(queue == NULL)
{
#if DEBUG
printf("squeue create error!\n");
#endif
return 0;
}
memset(queue->data,0,sizeof(queue->data));
queue->front = queue->rear =0;
return queue;
}
>3、顺序队列的入队
/**
* @description: 入顺序队列
* @param {squeue} queue-队列指针
* @param {data_t} value-入队列的值
* @return {0-函数失败,1-函数成功}
*/
int squeue_enter(squeue queue, data_t value)
{
if(queue == NULL)
{
#if DEBUG
printf("squeue create error!\n");
#endif
return 0;
}
if((queue->rear+1)%N == queue->front)
{
#if DEBUG
printf("squeue already full!\n");
#endif
return 0;
}
queue->data[queue->rear] = value;
queue->rear = (queue->rear+1)%N; //取余可以是值满后回到0
return 1;
}
>4、顺序队列的出队
/**
* @description:出队列
* @param {squeue} queue-队列指针
* @return {0-函数失败,value-出队列单元储存的值}
*/
int squeue_out(squeue queue)
{
data_t value;
if(queue == NULL)
{
#if DEBUG
printf("squeue create error!\n");
#endif
return 0;
}
if(queue->front == queue->rear)
{
#if DEBUG
printf("squeue is empty!\n");
#endif
return 0;
}
value = queue->data[queue->front];
queue->data[queue->front] = 0;
queue->front = (queue->front+1)%N;
return value;
}
>5、顺序队列的释放
/**
* @description: 队列释放
* @param {squeue} queue-队列指针
* @return {0-函数失败,1-函数成功}
*/
int squeue_free(squeue queue)
{
if(queue == NULL)
{
#if DEBUG
printf("squeue create error!\n");
#endif
return 0;
}
free(queue);
return 1;
}
>6、顺序队列的清空
/**
* @description: 队列清空
* @param {squeue} queue-队列指针
* @return {0-函数失败,1-函数成功}
*/
int squeue_clear(squeue queue)
{
if(queue == NULL)
{
#if DEBUG
printf("squeue create error!\n");
#endif
return 0;
}
memset(queue->data,0,sizeof(queue->data));
queue->front = queue->rear = 0;
return 1;
}
>7、队列是否为空的判断
/**
* @description: 判断队列是否为空
* @param {squeue} queue-队列指针
* @return {-1-函数失败,1-队列为空,0-队列不为空}
*/
int squeue_empty(squeue queue)
{
if(queue == NULL)
{
#if DEBUG
printf("squeue create error!\n");
#endif
return -1;
}
return queue->front == queue->rear? 1:0;
}
>8、队列是否为满的判断
/**
* @description: 判断队列是否满
* @param {squeue} queue
* @return {-1-函数失败,1-队列满,0-队列不满}
*/
int squeue_full(squeue queue)
{
if(queue == NULL)
{
#if DEBUG
printf("squeue create error!\n");
#endif
return -1;
}
if((queue->rear+1)%N == queue->front)
return 1;
else
return 0;
}
三、链表队列
>1、链表队列的原理
插入操作
在队尾
进行,删除操作
在队头
进行,由队头指针和队尾指针控制队列的操作。
因此链表队列
的结构体
定义为:
#define DEBUG 1
typedef int data_t;
typedef struct node
{
data_t data;
struct node *next;
}linklist_t,*linklist;
typedef struct
{
linklist front,rear;
}listqueue,*linkqueue;
>2、链表队列的操作演示
>3、链表队列的创建
/**
* @description: 链表队列创建
* @param {*}
* @return {lq-链表队列地址}
*/
linkqueue linkqueue_create()
{
linkqueue lq = (linkqueue)malloc(sizeof(listqueue));
if(lq == NULL)
{
#if DEBUG
printf("linkqueue create error!\n");
#endif
return 0;
}
lq->front = lq->rear = (linklist)malloc(sizeof(linklist_t));
if(lq->front == NULL || lq->rear == NULL)
{
#if DEBUG
printf("linkqueue create error!\n");
#endif
return 0;
}
lq->front->data = 0;
lq->front->next = NULL;
return lq;
}
>4、链表队列的入队
/**
* @description: 链表队列入队
* @param {linkqueue} lq-链表队列指针
* @param {data_t} value-入队列的值
* @return {0-函数失败,1-函数成功}
*/
int linkqueue_enter(linkqueue lq, data_t value)
{
linklist node = (linklist)malloc(sizeof(linklist_t));
if(lq == NULL)
{
#if DEBUG
printf("lq is NULL!\n");
#endif
return 0;
}
if(node == NULL)
{
#if DEBUG
printf("node create error!\n");
#endif
return 0;
}
lq->rear->next = node;
lq->rear = node;
node->data = value;
node->next = NULL;
return 1;
}
>5、链表队列的出队
/**
* @description:链表队列出队
* @param {linkqueue} lq-链表队列指针
* @return {0-函数失败,lq->front->data-出队列的值}
*/
data_t linkqueue_out(linkqueue lq)
{
linklist node;
if(lq == NULL)
{
#if DEBUG
printf("lq is NULL!\n");
#endif
return 0;
}
if(lq->front == lq->rear)
{
#if DEBUG
printf("lq is empty!\n");
#endif
return 0;
}
node = lq->front;
lq->front = node->next;
free(node);
return lq->front->data;
}
>6、链表队列的释放
/**
* @description: 链表队列释放
* @param {linkqueue} lq-链表队列指针
* @return {0-函数失败,1-函数成功}
*/
int linkqueue_free(linkqueue lq)
{
linklist node;
if(lq == NULL)
{
#if DEBUG
printf("lq is NULL!\n");
#endif
return 0;
}
while(lq->front)
{
node = lq->front;
lq->front = node->next;
free(node);
}
free(lq);
return 1;
}
>7、链表队列的清空
/**
* @description: 链表队列清空
* @param {linkqueue} lq-链表队列指针
* @return {0-函数失败,1-函数成功}}
*/
int linkqueue_clear(linkqueue lq)
{
linklist node;
if(lq == NULL)
{
#if DEBUG
printf("lq is NULL!\n");
#endif
return 0;
}
while(lq->front != lq->rear)
{
node = lq->front;
lq->front = node->next;
free(node);
}
return 1;
}
>8、判断链表队列是否为空
/**
* @description:判断链表队列是否为空
* @param {linkqueue} lq-链表队列指针
* @return {-1-函数失败,1-队列为空,0-队列不为空}
*/
int linkqueue_empty(linkqueue lq)
{
if(lq == NULL)
{
#if DEBUG
printf("lq is NULL!\n");
#endif
return -1;
}
return lq->front == lq->rear? 1:0;
}
四、链表的应用–球钟问题
1、球钟简介
球
钟是一个利用球的移动
来记录时间
的简单装置
它有
三个
可以容纳若干个球的指示器:分钟指示器
,五分钟指示器
,小时指示器
若分钟指示器中有2个球,五分钟指示器中有6个球,小时指示器中有5个球,则时间为5:32
2、球钟工作原理
- 每过
一分钟
,球钟就会从球队列
的队首
取出一个球
放入分钟指示器
,分钟指示器最多可容纳4个球
。 - 当放入
第五个球
时,在分钟指示器的4个球
就会按照他们被放入时
的相反顺序
加入球队列的队尾
。而第五个球
就会进入五分钟指示器
。 - 按此类推,
五分钟指示器
最多可放11个球
,小时指示器
最多可放11个球
。
3、问题阐述
- 当
小时指示器
放入第12个球
时,原来的11个球
按照他们被放入时
的相反顺序
加入球队列的队尾
,然后第12个球
也回到队尾
。 - 这时,
三个指示器
均为空,回到初始状态,从而形成一个循环
。因此,该球钟表示时间的范围是从0:00到11:59
。
问题:
现设初始时球队列的球数为27
,球钟的三个指示器初态均为空。要经过多久,球队列才能恢复
到原来的顺序
?
4、程序实现
这个问题我们选择顺序栈
来作为指示器
,链表队列
作为球队列
。实现代码如下:
/*
* @Author: your name
* @Date: 2022-03-11 16:23:40
* @LastEditTime: 2022-03-11 18:37:53
* @LastEditors: your name
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* @FilePath: \ballclock\test.c
*/
#include "linkqueue.h"
#include "sqstack.h"
int queue_check(linkqueue lq); //检查是否队列为有序队列
int main(int argc, const char *argv[])
{
linkqueue lq;
int i, min = 0;
data_t value;
sqstack *s_hour,*s_five,*s_min;
lq = linkqueue_create();
if(lq == NULL)
return 0;
s_hour = stack_create(11); //创建储存小时球的栈
if(s_hour == NULL)
return 0;
s_five = stack_create(11); //创建储存5分钟球的栈
if(s_five == NULL)
return 0;
s_min = stack_create(4); //创建储存分钟球的栈
if(s_min == NULL)
return 0;
for(i = 1; i < 28; i++) //将27颗数字球放入队列内
{
linkqueue_enter(lq, i);
}
while(1)
{
min++;
value = linkqueue_out(lq); //一分钟后取出一颗球
if(!stack_full(s_min)) //若分钟栈没满
stack_push(s_min, value); //存入分钟栈内
else
{
while(!stack_empty(s_min)) //分钟栈满了,取出栈内所有球
linkqueue_enter(lq, stack_pop(s_min));
if(!stack_full(s_five)) //若5分钟栈没满,下面步骤与上面类似
stack_push(s_five, value);
else
{
while(!stack_empty(s_five))
linkqueue_enter(lq, stack_pop(s_five));
if(!stack_full(s_hour))
stack_push(s_hour, value);
else
{
while(!stack_empty(s_hour))
linkqueue_enter(lq, stack_pop(s_hour));
linkqueue_enter(lq, value);
if(queue_check(lq)) //检查是否队列为有序队列
break;
}
}
}
}
printf("min=%d\n",min); //得到回归初始状态需要多少分钟
while(!linkqueue_empty(lq)) //打印出队列值
{
printf("%d ",linkqueue_out(lq));
}
puts("");
linkqueue_free(lq);
stack_free(s_min);
stack_free(s_five);
stack_free(s_hour);
return 0;
}
int queue_check(linkqueue lq)
{
linklist p;
if(lq == NULL)
{
printf("lq is NULL\n");
return 0;
}
p = lq->front->next;
if(p == NULL || p->next == NULL) //若队列没有值或者只有一个值
return 0;
while(p->next)
{
if(p->data > p->next->data)
return 0;
p = p->next;
}
return 1;
}
到这里就结束啦!
这里给出免费完整代码的下载链接:
队列C程序实现