队列的定义及其基本运用

 今天学习了队列的一些知识,接下来给出其特点,见下图:

从上面可以看出队列的三种基本操作分别是删除(队首),插入(队伍),以及新建一个队列,首先对于一个队列我们令队首指针通常为front而队尾指针为rear,同时当我们新建一个队列的时候通常令front=1,令rear=0,也就是说队列中元素的个数应该是rear-front+1,接下来给出一些题目对队列进行简单的理解:

代码如下:

#include<iostream>
using namespace std;
int m,p[100001],front=1,rear=0;
int main(){
	cin>>m;
	for(int i=1;i<=m;i++){
		char s[10];
		cin>>s;
		if(s[0]=='q'){
			int k;
			cin>>k;
			cout<<p[front+k-1]<<endl;
		}
	else if(s[1]=='u'){
		int x;
		cin>>x;
		p[++rear]=x;
	}
	else ++front;
	}
	return 0; 
}

 上面这一道题目只对队列涉及了简单的几个操作的运用,接下来是几道难度略微进阶,愈发综合的题目:

1.核酸队列:

#include<iostream>
using namespace std;
int n,q[101],front=1,rear=0;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int opt;
		cin>>opt;
		if(opt==1){
			char s[101];
			cin>>s;
			q[++rear]=s[0];
		}
		else for(int j=1;j<=10&&rear>=front;j++){
			cout<<(char)q[front++]<<' ';
		}
		if(opt==2) cout<<endl;
	}
	return 0;
}

2.队列练习:

#include<iostream>
using namespace std;
int x,k,front=1,rear=0;
long long a[3000001];
int main(){
	cin>>x>>k;
	a[++rear]=x;
	for(int i=1;i<=k;i++){
		cout<<a[front]<<endl;
		a[++rear]=2*a[front];
		a[++rear]=2*a[front]+1;
		front++;
	}
	return 0;
}

 3.排队买票:

上面这一道题相对复杂一些,有一些绕,下面我会给出代码并在代码中较为难以理解的部分用我自己的理解做出解释:

#include<iostream>
using namespace std;
int n,a[1001],p[1000001],ans[1001],front=1,rear;
int main(){
	cin>>n;
	rear=n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=n;i++) 
	p[i]=i;
	for(int t=1;rear>=front;t++){
		int x=p[front];
		a[x]--;
		++front;
		if(!a[x])
		ans[x]=t;
		else p[++rear]=x;
	}
	for(int i=1;i<=n;i++)
	cout<<ans[i]<<' ';
	return 0;
}

以下是我对这段代码的讲解,首先我们来分析一下题目的意思,就是有n个人买票,数组a中一共有n个元素,从1到n的下标对应的元素也就对应了编号为对应下标的人所需要的票数,接下来题目中说每个人一次性只能用一秒的时间来买一张票然后就需要排到队伍的末尾中去,所以我们需要知道一点就是我们在代码中当某一个人的票买完之后我们需要将这个人从队列中踢出去,也就是更新队列。接下来我将叙述上述代码的思路:首先定义的n代表了一共有多少人,a数组存储每个人需要买的票数,p数组用来更新队列的末尾,因为i每次队首的人买完票之后如果还有票需要买都要跑到队伍末尾继续买票,ans数组我用来存储每个人最后买票花费的总时间,队首一开始定义为1,队尾一开始没有输入n之前就先不赋值。

接下来我们对主函数里面的代码进行讲解:首先存储了人数n然后从1到n进行枚举并存储每个人买票需要花费的时间,然后将队尾赋值为n,并用p数组从1到n进行枚举表示每个下标对应和下标相同编号的人,然后我们来计算买票时间,从第一秒开始计时,这段是代码中的关键部分,首先用x来代换这时候的队首,然后将队首需要买的票数减去1,并更新队首,当a[x]等于0时,那么编号为x的人对应的ans也就是当前的时间t,否则我们将x对应的一开始的队首插入到队尾去,这样一直枚举到每个人的票数都买完即可。

最后从1到n挨着进行输出对应编号人买票所需要花费的总时间即可。

实际上还有一种方法,下面是代码,我在下面会告知我个人的理解和思路:

#include<iostream>
using namespace std;
int n,a[1001];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=n;i++){
	int ans=a[i];
	for(int j=1;j<i;j++)
	ans+=min(a[i],a[j]);
	for(int j=i+1;j<=n;j++)
	ans+=min(a[i]-1,a[j]);
	cout<<ans<<' ';
	}
	return 0; 
}

很明显发现代码的量减少了,接下来我会解释其中的奥妙:

首先这个题目是n个人排队,相当于n个人会循环着买票,其中当某个人买到自己需要数量的票之后就不再继续跟着循环,就好比题目中给出的四个人的样例,都是按照1,2,3,4这样的循环挨着买的,我们可以发现,一个人的买票间隔其实也就是他身后排着的人的数量,并且由于每个人买到自己想要的票数之后就会离开队列所以我们其实可以发现,一个人的买票总时间可以表示成前后小于他买票时间的和,当前后某人的买票时间大于等于他时,那么就加上他自己的买票时间即可,同时请注意排在他后面的人的买票时间应该和他本身的买票时间减去一来比较,这是因为他已经买过一次票了,所以需要减去一再和别的人比较。

可能解释起来比较抽象不过多想想也肯定能想明白其中的道理。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

残念亦需沉淀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值