[刷题笔记] 关于栈 & 队列常用操作方法的再探索

Part 0:序

其实本来这都是很基础的东西,可惜野路子出身基础并不扎实。

借着这个机会整合一下吧。也做了一些题了解了一些基本操作方法。

本文对于 栈 和 队列的原理不再过多赘述,默认读者掌握基本原理。

参考题单:
数据结构加强001:栈和队列

2024现代信息学测试1:栈和队列

本文所讲例题来源以上题单。

【团队内部题单】

所谓 “栈” 就是一种先进后出的数据结构,最典型的,可以用来进行运算顺序的处理。我们只需要在计算乘法的时候将 stack 顶弹出和当前数据乘法运算后再入栈即可。

典例:P1981 表达式求值

上述是栈的基本操作。接下来会给出几个例题来进一步强化栈的操作。

典例1:P4387 验证栈序列

Problem

Description

给定入栈序列 \(s\),和一个序列 \(k\),你需要判断在入栈顺序为 \(s\) 的前提下,出栈顺序是否可能为 \(k\)

Analysis
Analysis

注意到我们需要判断出栈序列是否合法,显然我们可以直接递归搜索每一种可能的出栈顺序,然后比对即可。本题没有给定数据范围,但是会 TLE。

考虑“贪心地”去尽可能满足它所给定的出栈序列。

具体地,每次入栈后,如果此时的栈顶元素是当前应当出栈的(也就是给定出栈序列),则出栈。需要注意每次入栈后可以连续出栈,所以我们需要 while 循环,而不是默认只能出一个元素。

如此操作,我们确保了每次出栈操作是合法的。最后判断栈是否为空即可。

对于本题,我们所有的操作都可以在 main 函数里解决,所以 stack 开局部变量即可。避免忘记多测清空。

本题的本质就是模拟栈操作。

Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 100010;
int a[N],b[N];
int q;
stack <int> s;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>q;
	while(q--)
	{
		int cnt = 1;
		int n;
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) cin>>b[i];
		for(int i=1;i<=n;i++)
		{
			s.push(a[i]);
			while(!s.empty() && s.top() == b[cnt])
			{
				s.pop();
				cnt++;
			}
		}
		if(s.empty()) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
		while(!s.empty()) s.pop();
	}
	return 0;
}

典例2:P3056

Description

Problem

给出一个偶数长度的括号序列,问最少修改多少个括号可以使其平衡。

Analysis
Analysis

首先明确,本题是 修改括号而不是括号配对! 题意需要明确。

然后如何处理?我们不妨先将配对的括号全部配对,这很简单,使用 stack 即可完成。

接下来我们会剩下些许左括号和右括号,分别记为 \(s_1,s_2\)

接下来考虑如何将这些 无法配对 的括号通过一系列修改使其配对。

考虑手动构造几组样例,如下:

image

(图1)
image
(图2)

通过构造样例,不难发现 对于无法配对的括号,只有可能是一连串的左括号和一连串的右括号,不可能出现形似 ”()“的,原因显然。

考虑对于这样的括号如何配对。

发现对于图1,左括号和右括号都是 偶数个,贪心地考虑,我们可以将左括号和右括号间隔排列,也就是需要更改 \(s_1\div 2 + s_2\div 2\) 个括号。

对于图2,像图1一样构造后一定会剩下一个 ")(",这样我们只能通过添加括号来配对,只需要在 \(s_1\div 2 + s_2\div 2\) 的基础上 \(+2\) 即可。由于 C++ 默认向 0 取整,对于正数也就是向下取整,所以无需特殊处理。

至此,本题的解。考虑到最后发现本题是个人类智慧贪心构造题!只不过在贪心前一定要处理好配对好的括号。

Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
stack <int> s;
int num = 0;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    char ch;
    int cnt = 0;
    while(cin>>ch)
    {
        if(ch == '(') s.push(++cnt);
        else
        {
            if(!s.empty()) s.pop();
            else num ++;
        }
    }
    if(num % 2 == 0 && s.size() % 2 == 0) cout<<num/2+s.size()/2<<endl;
    else cout<<num/2+s.size()/2+2<<endl;
    return 0;
}

典例3:P2866

Description

image

Analysis

本题是单调栈的模板题,参见:SXqwq 的单调栈学习笔记

队列

所谓 "队列" 是一种先进先出的数据结构。具体地,队列一端进,一端出。

诚然,这只是最基础地队列,队列只是一种模型,大家千万不要被束缚。下面列举了几个基础应用。

STL 板子

P2952

Analysis

没啥好分析的,队列的两端都可以进出,直接 deque 即可。

Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <deque>
using namespace std;
int n;
int cow_cnt = 1;
deque <int> q;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        char ctl;
        cin>>ctl;
        if(ctl == 'A')
        {
            char ctl2;
            cin>>ctl2;
            if(ctl2 == 'L') q.push_front(cow_cnt++);
            else q.push_back(cow_cnt++);
        }
        else if(ctl == 'D')
        {
            char ctl2;
            int k;
            cin>>ctl2;
            if(ctl2 == 'L')
            {
                cin>>k;
                while(k--)
                {
                    q.pop_front();
                }
            }
            else 
            {
                cin>>k;
                while(k--)
                {
                    q.pop_back();
                }
            }
        }
    }
    while(!q.empty())
    {
        cout<<q.front()<<endl;
        q.pop_front();
    }
    return 0;
}

典例2:P3662

P3662

Description

共有N个信号灯,编号为1~N,有B个信号灯损坏,给你它们的编号。

问,最少修好几个信号灯,可以有K个编号连续的信号灯。

Analysis

Analysis

记每个需要修理的路灯为 \(1\) ,其他为 \(0\)

题目要求 最少修好多少个灯,可以有 \(K\) 个编号连续的信号灯

不妨首先算 \(1-k\) 号路灯有几个需要修的,然后滑块移动即可。

当然也有用前缀和处理的,我当时没想到ww

Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N  = 100010;
const int INF = 0x3f3f3f3f;
int n,k,b;
int num[N];
int sum;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>k>>b;
	for(int i=1;i<=b;i++)
	{
		int p;
		cin>>p;
		num[p] ++;
	}
	for(int i=0;i<=k;i++) sum += num[i];
	int l = 0,r = k;
	int minn = INF;
    minn = min(minn,sum);
	while( r <= n)
	{
     //   cout<<l<<" "<<r<<endl;
     l ++;
		r ++;
        minn = min(minn,sum);
		sum -= num[l];
		sum += num[r];
		minn = min(minn,sum);
	}
	cout<<minn<<endl;
	return 0;
}
posted @   SXqwq  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示