栈和队列(手写)

  • Post author:
  • Post category:其他


其实已经发过一次栈和队列了,不过那个是直接利用STL的用法,这里写一写利用数组完成的栈和队列操作,顺便写一写

单调栈



单调队列

的操作和一般用途。



1.栈和队列基本特点

没啥好讲的,栈是FILO,队列是FIFO, 偷个懒,需要的

点这里。



2.栈



<1>手写栈

我们一般用数组来存储栈,我通常写作stk[i],然后用tt来表示栈顶,实现代码一系列操作。

const int N = 100010;
int stk[N], tt = 0;

// 压入元素x
stk[++ tt] = x;

// 弹出栈顶元素
tt --;

// 调用栈顶元素的值
auto top = stk[tt];

// 判断栈是否为空
if(tt) not empty;
else empty

对的,就是那么的简单,只有这些操作。主要是理解思想,模板随便记。



<2>单调栈

单调的意思其实也就是里面的元素大小具有单调性(递增或者递减),操作其实和普通栈的一样,这里主要理解它的思想和用法,所以直接用例题来看。




例:单调栈

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


输入格式


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

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


输出格式


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


数据范围


1≤N≤1051≤N≤105

1≤数列中元素≤1091≤数列中元素≤109


输入样例:


5

3 4 2 7 5


输出样例:


-1 3 -1 2 2

这道题的暴力思想是,把每个数据压入栈中,然后遍历之前的数据,再压入下一个数据。很显然,这样的操作是很慢的,我们的复杂度是在O(n^2)的。所以我们换一下思路,再每压入一个新数据是,就与前面的数据比较,如果前面的数据比它大或等于它,那之前的数据就绝不再会出现在后面的答案中,直接删除pass掉,这样就可以很大优化我们的算法。因为每个元素至多进栈一次,出栈一次,所以我们的复杂度就变成了O(2n),2可以放在前面,这样我们的复杂度等级就降低到了O(n),优化了很多。

下面是AC代码

#include<bits/stdc++.h>
using namespace std;

const int N = 100010;
int n, x;
int stk[N], tt = 0;

int main()
{
	cin.tie(0);   // 这是一个刚get到的输入优化操作,蛮好用的
	cin >> n;
	while(n --)
	{
		cin >> x;
		while(tt && stk[tt] >= x) tt --;
		if(tt) cout << stk[tt] << ' ';
		else cout << -1 << ' ';
		stk[++ tt] = x;
	}
	return 0;
}

标题上有原题链接,可以自己点开做一做,交一交。



3.队列



<1>手写队列

队列的操作同样是用数组完成的,我们用que[i]代表队列,hh代表队头,tt代表队尾。

const int  N = 100010;
int que[N], hh = 0, tt = -1;

// 插入操作
que[++ tt] = x;

//  弹出
hh ++// 调用队头(也可调用队尾)
auto head = que[hh]auto tail = que[tt];

//判断是否为空
if(hh <= tt) not empyt;
else empty; 



<2> 单调队列

单调队列,用法和单调栈差不多,我们也直接来看题目。




例:滑动窗口

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

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

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

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

以下是一个例子:

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

窗口位置

最小值

最大值

[1 3 -1] -3 5 3 6 7

-1

3

1 [3 -1 -3] 5 3 6 7

-3

3

1 3 [-1 -3 5] 3 6 7

-3

5

1 3 -1 [-3 5 3] 6 7

-3

5

1 3 -1 -3 [5 3 6] 7

3

6

1 3 -1 -3 5 [3 6 7]

3

7

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


输入格式


输入包含两行。

第一行包含两个整数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

这题,我们同样也是先想出暴力求解的方法,然后再优化。过程差不多,就不再分析了。不过这道题有一点不同的是它是利用下标来对窗口中的元素进行维护的,并没有直接用值。

这里是AC代码:

#include<bits/stdc++.h>

const int N = 1e6 + 8;
int que[N], hh = 0, tt = -1;
int n, k, a[N];

int main()
{
	cin.tie(0);
	cin >> n >> k;
	for(int i = 0; i < n; i ++)
		cin >> a[i];
	for(int i = 0; i < n; i ++)
	{
		if(hh <= tt && i - k + 1 > que[hh]) hh ++;
		while(hh <= tt && a[que[tt]] >= a[i]) tt --;
		que[++ tt] = i;
		if(i >= k - 1) printf("%d ", a[que[hh]]);
	}
	printf("\n");
	hh = 0, tt = -1;
	for(int i = 0; i < n; i ++)
	{
		if(hh <= tt && i - k + 1 > que[hh]) hh ++;
		while(hh <= tt && a[que[tt]] <= a[i]) tt --;
		que[++ tt] = i;
		if(i >= k - 1) printf("%d ", a[que[hh]]);
	}
	printf("\n");
	return 0;
}

嗯,就是这样,一点都不难,多刷题就会了QwQ

加油QwQ



版权声明:本文为zhachengxi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。