0
点赞
收藏
分享

微信扫一扫

yxc_第二章 数据结构(一)_栈和队列

技术只适用于干活 2022-02-02 阅读 79
数据结构

目录

一、栈和队列的代码操作

1、AcWing 828 模拟栈

2、AcWing 829 模拟队列

二、单调栈

1、AcWing 830 单调栈

三、单调队列

1、AcWing 154 滑动窗口


一、栈和队列的代码操作

1、AcWing 828 模拟栈

实现一个栈,栈初始为空,支持四种操作:

  1. push x – 向栈顶插入一个数 x;
  2. pop – 从栈顶弹出一个数;
  3. empty – 判断栈是否为空;
  4. query – 查询栈顶元素。

现在要对栈进行 M 个操作,其中的每个操作 3 和操作 4 都要输出相应的结果。

输入格式

第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令为 push xpopemptyquery 中的一种。

输出格式

对于每个 empty 和 query 操作都要输出一个查询结果,每个结果占一行。

其中,empty 操作的查询结果为 YES 或 NOquery 操作的查询结果为一个整数,表示栈顶元素的值。

数据范围

1≤M≤100000
1≤x≤109
所有操作保证合法。

输入样例:

10
push 5
query
push 6
pop
query
pop
empty
push 4
query
empty

输出样例:

5
5
YES
4
NO
#include<iostream>
using namespace std;

const int N = 100010;

int stk[N], tt;//stk[N]为数组模拟栈,tt为指针指向栈顶元素

int main()
{
int m;
cin >> m;

while (m -- )
{
string op;
cin >> op;

//插入
if (op == "push")
{
int x;
cin >> x;
stk[ ++ tt] = x;
}
else if (op == "pop") tt -- ;//弹出
else if (op == "empty") cout << (tt ? "NO" : "YES") << endl;//判断栈是否为空
else cout << stk[tt] << endl;//输出栈顶元素
}

return 0;
}

2、AcWing 829 模拟队列

实现一个队列,队列初始为空,支持四种操作:

  1. push x – 向队尾插入一个数 x;
  2. pop – 从队头弹出一个数;
  3. empty – 判断队列是否为空;
  4. query – 查询队头元素。

现在要对队列进行 M 个操作,其中的每个操作 3 和操作 4 都要输出相应的结果。

输入格式

第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令为 push xpopemptyquery 中的一种。

输出格式

对于每个 empty 和 query 操作都要输出一个查询结果,每个结果占一行。

其中,empty 操作的查询结果为 YES 或 NOquery 操作的查询结果为一个整数,表示队头元素的值。

数据范围

1≤M≤100000
1≤x≤109
所有操作保证合法。

输入样例:

10
push 6
empty
query
pop
empty
push 3
push 4
pop
query
push 6

输出样例:

NO
6
YES
4
#include<iostream>
using namespace std;

const int N = 100010;

int q[N], hh, tt = -1;//hh指向队头元素,tt指向队尾元素

int main()
{
int m;
cin >> m;

while (m -- )
{
string op;
cin >> op;

//在队尾插入元素
if (op == "push")
{
int x;
cin >> x;
q[ ++ tt] = x;
}
else if (op == "pop") hh ++ ;//弹出队头元素,注意这里是++而不是--
else if (op == "empty") cout << (hh <= tt ? "NO" : "YES") << endl;//判断队列是否为空
else cout << q[hh] <<endl;//输出队头元素
}

return 0;
}

二、单调栈

单调栈比较抽象,但是会考察到的题型并不多,几乎只考察AcWing 830这样的题型,所以我只针对题来训练。

1、AcWing 830 单调栈

给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。

输入格式

第一行包含整数 N,表示数列长度。

第二行包含 N 个整数,表示整数数列。

输出格式

共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。

数据范围

1≤N≤105
1≤数列中元素≤109

输入样例:

5
3 4 2 7 5

输出样例:

-1 3 -1 2 2
#include<iostream>
using namespace std;

const int N = 100010;

int stk[N], tt;

int main()
{
int m;
cin >> m;

while (m -- )
{
int x;
cin >> x;

while (tt && stk[tt] >= x) tt -- ;

if (!tt) cout << "-1 ";
else cout << stk[tt] << " ";

stk[ ++ tt] = x;
}

return 0;
}

所谓单调栈,其实就是为了输出每个数左边最近的比它小的数字,而利用的工具。我们对于数列中的每一个数在进栈之前先进行一个判断:如果栈为空,则其左边没有数字,更谈不上找比x小的数;如果栈非空且栈顶元素大于等于x ,则弹出栈顶元素(因为由于x的存在,栈顶元素将永远不会被输出,留在栈中反而碍事)。通过不断弹出比x大的栈顶元素,使得while循环结束后,栈顶元素一定小于x。这样操作得到的栈可以一直保持元素单调递增,故称单调栈

三、单调队列

1、AcWing 154 滑动窗口

给定一个大小为 n≤106 的数组。

有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。

你只能在窗口中看到 k 个数字。

每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。

窗口位置最小值最大值
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37

你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式

输入包含两行。

第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。

第二行有 n 个整数,代表数组的具体数值。

同行数据之间用空格隔开。

输出格式

输出包含两个。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

输入样例:

8 3
1 3 -1 -3 5 3 6 7

输出样例:

-1 -3 -3 -3 3 3
3 3 5 5 6 7

在不使用单调队列优化之前,暴力做法的时间复杂度为O(nk)。 

#include<iostream>
using namespace std;

const int N = 1000010;

int n;
int a[N], q[N];

int main()
{
int k;
scanf("%d%d",

for (int i = 0; i < n; i ++ ) scanf("%d",

int hh = 0, tt = -1;

for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
//这里保证队列中的元素始终是窗口中元素的子集
while (hh <= tt && a[q[tt]] >= a[i]) tt -- ;
//保证队列单调递增,即队头元素为窗口中元素的最小值
q[ ++ tt] = i;
//q[N]数组中存储的是数列元素的下标
if (i >= k - 1) printf("%d ", a[q[hh]]);
//当i>=k-1时(窗口被元素填满的起始位置),输出队头元素
//a[q[hh]]表示窗口元素中数值最小的数
}

puts("");//回车

hh = 0, tt = -1;//初始化

for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
//保证队列单调递减,即队头元素为窗口中元素的最大值
q[ ++ tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
//当i>=k-1时(窗口被元素填满的起始位置),输出队头元素
//a[q[hh]]表示窗口元素中数值最大的数
}

puts("");//回车

return 0;
}

 hh <= tt 可以保证在队列为空的时候不执行语句,先加入元素进队列。

举报

相关推荐

0 条评论