【数据结构】循环队列

1.队列

**队列:**先进先出的线性表,受到限制的,一端进行入队,一端进行出队。
入队的一端叫做队尾,出队的一端叫做队头,队列中没有元素的话,叫做空队。
在这里插入图片描述

2.循环队列

循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
在循环队列中,当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。为了区别这两种情况,规定循环队列最多只能有 MaxSize-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。因此,队列判空的条件是front=rear,而队列判满的条件是front=(rear+1)%MaxSize。

循环队列有三个难点:

第一个:如何保证队列的入队和出队时间复杂度都为O(1)
第二个:如何保证判空和判满的条件不冲突?
第三个:如何求循环队列有效长度?

我们可以模拟火车上售货模式,也就是说,出队和入队的时候,数据不需要挪动,直接挪动队头指针和队尾指针即可

在这里插入图片描述
现在这种模式确实可以让队列的入队和出队时间复杂度都为O(1)
但是这种设计,会引发第二个问题?
判空和判满,会发生冲突,因为判断条件都是 队头==队尾

在这里插入图片描述
在这里插入图片描述
解决方案:
我们可以臆想成一个环形:实际上还是一个数组
在这里插入图片描述
判空和判满,会发生冲突,因为判断条件都一样

有两种方法可以解决:

第一种: 额外申请一个变量count,保存有效值个数
判空:队头 == 队尾 且 count== 0
判满:队头 == 队尾 且 count !=0

**第二种:**将循环队列中队尾处,浪费一个空间不用,这个浪费的空间不再保存有效值,而是作为一个标记位
判空: 队头==队尾
判满: 队尾向后走一步 遇到了队头,则判满
在这里插入图片描述
动态位是动态变化的

有效长度为 6 ,怎么得到时间复杂度为O(1)?

如果:队尾下标>队头下标 rear - front
在这里插入图片描述
如果:队尾<队头 rear - front + MAX_SIZE
在这里插入图片描述

那么 我们有了这种想法以后,就无需判断队尾和队头谁更大,直接使用下面的这个公式即可:

循环队列求长度公式 : ( rear - front + MAX_SIZE ) % MAX_SIZE

+MAX_SIZE : 防止 rear-front 出现负数

%MAX_SIZE:防止rear-front不是负数,导致+MAX_SIZE加多了****

3.循环队列头文件与函数声明

#pragma once 

//循环队列的结构体设计:

typedef int ELEM_TYPE;
#define MAX_SIZE 100

typedef struct Queue
{
	ELEM_TYPE *base;//一个数组(用于保存数据)
	int front;//队头指针(保存数组下标,INT)
	int rear;//队尾指针(保存数组下标,INT)
}Queue, *PQueue;



//增删改查
//初始化
void Init_queue(struct Queue * qu);

//入队
bool Push(PQueue qu, ELEM_TYPE val);

//出队(既要返回出队的值,还有返回操作是否成功,所以还需要一个输出参数)
bool Pop(PQueue qu, ELEM_TYPE *rtval);

//获取队头值
bool Top(PQueue qu, ELEM_TYPE *rtval);

//判空
bool IsEmpty(PQueue qu);

//判满
bool IsFull(PQueue qu);

//查找
int Search(PQueue qu, ELEM_TYPE val);

//获取队列有效长度
int Get_length(PQueue qu);

//清空
void Clear(PQueue qu);

//销毁
void Destroy(PQueue qu);

//打印
void Show(PQueue qu);

4.函数

4.1初始化

从堆区申请空间,初始化队头与队尾指针

//初始化
void Init_queue(struct Queue * qu)
{
	//assert
	qu->base = (ELEM_TYPE *)malloc(MAX_SIZE * sizeof(ELEM_TYPE));
	assert(qu->base != NULL);

	qu->front = qu->rear = 0;
}

4.2入队

通过队头指针和队尾指针入队

//入队
bool Push(PQueue qu, ELEM_TYPE val)
{
	//assert
	if(IsFull(qu))
	{
		return false;
	}

	qu->base[qu->rear] = val;
	//qu->rear++;//error
	qu->rear = (qu->rear+1)%MAX_SIZE;

	return true;
}

4.3出队

//出队(既要返回出队的值,还有返回操作是否成功,所以还需要一个输出参数)
bool Pop(PQueue qu, ELEM_TYPE *rtval)
{
	//assert  qu  rtval
	if(IsEmpty(qu))
	{
		return false;
	}
	*rtval = qu->base[qu->front];

	//qu->front++;//error
	qu->front = (qu->front+1)%MAX_SIZE;
	return true;
}

4.4获取队头值

//获取队头值
bool Top(PQueue qu, ELEM_TYPE *rtval)
{
	//assert  qu  rtval
	if(IsEmpty(qu))
	{
		return false;
	}

	*rtval = qu->base[qu->front];

	return true;
}

4.5判空与判满

//判空
bool IsEmpty(PQueue qu)
{
	return qu->front == qu->rear;
}

//判满   如果队尾向后再走一步,遇到了队头,此时则认为队列已经满了
bool IsFull(PQueue qu)
{
	//assert

	return (qu->rear+1)%MAX_SIZE == qu->front;
}

4.6查找

//查找
int Search(PQueue qu, ELEM_TYPE val)
{
	//assert
	//实际上是一个数组,所以将其遍历一般,即可
	for(int i=qu->front; i!=qu->rear; i=(i+1)%MAX_SIZE)
	{
		if(val == qu->base[i])
		{
			return i;
		}
	}
	
	return -1;
}

4.7获取队列有效长度

//获取队列有效长度
int Get_length(PQueue qu)
{
	return (qu->rear - qu->front + MAX_SIZE) % MAX_SIZE;
}

4.8清空与销毁

//清空
void Clear(PQueue qu)
{
	qu->front = qu->rear = 0;
}

//销毁
void Destroy(PQueue qu)
{
	free(qu->base);
	qu->base = NULL;//避免二次销毁
	qu->front = qu->rear;
}

5.循环队列源文件与函数实现

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Queue.h"


//增删改查
//初始化
void Init_queue(struct Queue * qu)
{
	//assert
	qu->base = (ELEM_TYPE *)malloc(MAX_SIZE * sizeof(ELEM_TYPE));
	assert(qu->base != NULL);

	qu->front = qu->rear = 0;
}

//入队
bool Push(PQueue qu, ELEM_TYPE val)
{
	//assert
	if(IsFull(qu))
	{
		return false;
	}

	qu->base[qu->rear] = val;
	//qu->rear++;//error
	qu->rear = (qu->rear+1)%MAX_SIZE;

	return true;
}

//出队(既要返回出队的值,还有返回操作是否成功,所以还需要一个输出参数)
bool Pop(PQueue qu, ELEM_TYPE *rtval)
{
	//assert  qu  rtval
	if(IsEmpty(qu))
	{
		return false;
	}
	*rtval = qu->base[qu->front];

	//qu->front++;//error
	qu->front = (qu->front+1)%MAX_SIZE;
	return true;
}

//获取队头值
bool Top(PQueue qu, ELEM_TYPE *rtval)
{
	//assert  qu  rtval
	if(IsEmpty(qu))
	{
		return false;
	}

	*rtval = qu->base[qu->front];

	return true;
}

//判空
bool IsEmpty(PQueue qu)
{
	return qu->front == qu->rear;
}

//判满   如果队尾向后再走一步,遇到了队头,此时则认为队列已经满了
bool IsFull(PQueue qu)
{
	//assert

	return (qu->rear+1)%MAX_SIZE == qu->front;
}


//查找
int Search(PQueue qu, ELEM_TYPE val)
{
	//assert
	//实际上是一个数组,所以将其遍历一般,即可
	for(int i=qu->front; i!=qu->rear; i=(i+1)%MAX_SIZE)
	{
		if(val == qu->base[i])
		{
			return i;
		}
	}
	
	return -1;
}

//获取队列有效长度
int Get_length(PQueue qu)
{
	return (qu->rear - qu->front + MAX_SIZE) % MAX_SIZE;
}

//清空
void Clear(PQueue qu)
{
	qu->front = qu->rear = 0;
}

//销毁
void Destroy(PQueue qu)
{
	free(qu->base);
	qu->base = NULL;//避免二次销毁
	qu->front = qu->rear;
}

//打印
void Show(PQueue qu)
{
	//assert
	//实际上是一个数组,所以将其遍历一般,即可
	for(int i=qu->front; i!=qu->rear; i=(i+1)%MAX_SIZE)
	{
		printf("%d ", qu->base[i]);
	}
	printf("\n");
}
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Shang_Jianyu_ss

感谢大哥

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

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

打赏作者

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

抵扣说明:

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

余额充值