【数据结构与算法】程序内功篇五--队列


一、队列

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程序实现


在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

修成真

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

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

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

打赏作者

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

抵扣说明:

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

余额充值