数据结构基础——队列

队列

队列是只允许在一段进行插入操作,另一端进行删除操作的线性表
队列是一种先进先出的线性表,简称FIFO,允许插入的一端是队尾,允许删除的一段是队首。队列在程序运行设计中用的十分频繁,,比如键盘进行的各种字母数字的输入输出。

循环队列

1.队列顺序储存的不足

假设一个队列有n个元素,我们就需要一个大于n的数组,插入操作很好理解,就是在队尾的下一个位置储存一个数据,但如果是删除数据呢?显然我们有两种方式。
一种就是将队首删除,然后每个元素向前移动一位,但是这样的时间复杂度是O(n)。
可是想想,为什么出队一定要移动所有元素呢?如果我们不去限制队首的位置,也就是说我们不必规定队列的元素必须限制在数组前n个单元内,我们就能用O(1)的时间复杂度来删除一个元素,为了避免当只有一个元素时,队头和队尾重合使处理变得麻烦,所以引入两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个地址,这样当front和rear重合时,此时队列为空。
例如:如果有一个长度为5的数组a[5],一个队列储存在a[0]~a[4],front指向0,rear指向5,如果我们想删除元素,只需要使front+1指向1即可,但是随之而来的又是一个问题,如果此时我们想插入一个元素呢?如果a[5]储存插入的元素,那么rear指向哪呢,此时就会发生一个数组越界的错误,但是明明a[0]是空的,我们把这种现象叫做假溢出。此时我们就需要循环队列了

2.循环队列的定义

试想:如果我们做公交车的时候,后面没有座位了,但是前面还有几个座位,那么你是不坐了呢还是坐到前面去呢?显然是坐到前面去,循环队列用的也是这个思想
继续上面的问题如果我们想继续插入a[5]的话我们只需要让rear指向0,即可完成一个循环
但是问题又双来了,当rear=front时,他们又可能是重合,也有可能是相差一圈 ,那么怎么判断队列是空是满呢?
如果队列最大尺寸是s,那么队列满的条件就是(rear+1)%s==front
在上面的例子中front== 1, rear==0,满足上面的式子,所以队列已经满了
循环队列代码如下

typedef int qelemtype;
typedef struct
{
	qelemtype data[maxsize];
	int front;//头指针
	int rear;//尾指针,指向队尾元素的下一个位置
}sqqueue;
//循环队列q初始化
Status initqueue(sqqueue* q)
{
	q->front = 0;
	q->rear = 0;
	return 1;
}
//循环队列求队列长度代码
int queuelength(sqqueue q)
{
	return (q.rear - q.front + maxsize) % maxsize;
}
//循环队列入列操作 
Stauts inqueue(sqqueue* q, qelemtype e)
{
	if ((q->rear + 1) % maxsize == q->front;)//判断队列是否已经满了
		return 0;
	q->data[q->rear] = e;
	q->rear = (q->rear + 1) % maxsize;//*rear指针向后移一位
	return 1;
}
//循环队列的出队操作
Status dequeue(sqqueue* q, qelemtype* e)
{
	if (q->front == q->rear)//判断队列是否为空
		return 0;
	*e = q.->data[q->front];
	q->front = (q->front + 1) % maxsize;
	return 1;
}

队列的数组模拟

int q[maxn], tail = 0, head = 1;

void push(int x)
{
	q[++tail] = x;
}
void pop()
{
	head++;
}
int gethead()
{
	return q[head];
}
int gettail()
{
	return q[tail];
}

单调队列

顾名思义,单调队列就是数据单调递增或者递减的队列
模板题:洛谷p1886
在这里插入图片描述

//这里以取最大值为例,取最小值留给读者自行考虑
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1005;
struct data
{
	int val, pos;
}a[maxn];
int n, k;
int q[maxn];
int tail = 0;
int head = 1;
void push(int x)
{
	while (a[x].val >= a[q[tail]].val && head >= tail)
		head--;
	while (a[x].pos - a[q[tail]].pos >= k && head >= tail)tail++;
	q[++head] = x;

}
int main()
{
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i].val;
		a[i].pos = i;
	}
	q[++tail] = 1;
	if (k == 1)
		printf("%d", a[q[tail]].val);
	for (int i = 2; i <= n; i++)
	{
		push(i);
		if (i > k)cout << a[q[tail]].val;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青云遮夜雨

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

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

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

打赏作者

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

抵扣说明:

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

余额充值