单调栈初步学习

1.定义

从栈底元素到栈顶元素呈单调递增或单调递减,栈内序列满足单调性的栈;

2.原理

(1)当新元素在单调性上优于栈顶时(单增栈新元素比栈顶大(升序)单减栈新元素比栈顶小 (降序) ),压栈,栈深+1;

(2)当新元素在单调性与栈顶相同(新元素于栈顶相同)或劣于栈顶时(单增栈新元素比栈顶小,单减栈新元素比栈顶大),弹栈,栈深-1;

模拟实现一个在原输入数据的顺序基础上递减的单调栈:

现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。

  • 10入栈时,栈为空,直接入栈,栈内元素为10。
  • 3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。
  • 7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。
  • 4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。
  • 12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。

3.代码实现

输入

5
10 3 7 4 12

升序排列的单调栈(不重复)

#include <bits/stdc++.h>

using namespace std;

const int N=1e6;
int stk[N],tt=0;

int main()
{
    int n,x;
    cin>>n;
    for (int i = 0; i < n; ++i) {
        cin>>x;      //升序,要求其左边的数必须小于x,是否取等号?取决于是否重复
        while (tt && x<=stk[tt]) tt--; //出栈
        stk[++tt]=x;//入栈
    }
    //输出模拟栈中的元素
    for (int i = 1; i <= tt; ++i)  cout<<stk[i]<<" ";
    return 0;
}

输出:

3 4 12

降序排列

仅第14行的符号变了。

#include <bits/stdc++.h>

using namespace std;

const int N=1e6;
int stk[N],tt=0;

int main()
{
    int n,x;
    cin>>n;
    for (int i = 0; i < n; ++i) {
        cin>>x;     //降序,要求其左边的数必须大于x
        while (tt && x>=stk[tt]) tt--; //出栈
        stk[++tt]=x;//入栈
    }
    //输出模拟栈中的元素
    for (int i = 1; i <= tt; ++i)  cout<<stk[i]<<" ";
    return 0;
}

输出:

12

4.应用

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 <bits/stdc++.h>

using namespace std;

const int N=1e5+10;
int a[N];

int main() {
    int n;
    cin>>n;
    for (int i = 0; i < n; ++i)  cin>>a[i];
    for (int i = 0,j; i < n; ++i) {
        for (j = i-1; j >= 0; --j) {
            if (a[j]<a[i]) {
                cout<<a[j]<<" ";  break;
            }
        }
        if (j==-1) cout<<"-1 ";
    }
    return 0;
}

我们根据这个过程,观察到一些性质,一些数可能再也不会被用到

在查找过程中,我们是从第1个数a[0]开始查找的,

第1个数左边肯定不存在数,输出-1,并把a[0]入栈;

第2个数a[1]时,现在开始判断a[1]和a[0]的关系,分两种情况:

  1. a[1]>a[0] ,此时a[0]符合题目要求:是a[1]左边的第一个比它小的数,可以输出,并把a[1]入栈。
  2. a[1]<=a[0],此时a[1]左边不满足符合要求的数,那么输出-1,同时由于a[1]<=a[0],所以a[0]就是那个再也不会被用到的数,因为一旦a[0]是符合小于a[i](i>=2),那么a[1]必然符合,且比a[0]靠近a[i],所以不仅要输出-1,还要把a[0]弹出(因为用不到了),再把a[1]入栈。

第3个数a[2],有了第二步的判断,现在只用判断a[2]和a[1]的关系即可:(a[1]一定是左边里面最大的),然后具体的操作情况都和第2步一样。

越往后,栈里面的元素可能有多个,如果a[i]<=a[i-1],a[i-1]被弹出后,不能停止,还要继续判断a[i]与a[i-2]的关系,直到找到满足要求的数或者栈为空为止。即如果栈不为空并且a[i]<=a[i-k],就一直出栈,更适合while语句。

最终,我们得到的是一个在原输入数据的顺序基础上,得到的一个升序的存储数据的栈。

代码实现

#include <bits/stdc++.h>

using namespace std;

const int N=1e5+10;
int a[N],tt;  //这里采用数组模拟栈来操作,从下标1开始存储

int main()
{
    int n,x;
    scanf("%d",&n);
    for (int i = 0; i < n; ++i) {
        scanf("%d",&x);
        while(tt && x <= a[tt])  tt--;  //找到满足要求的数或者栈为空再停下来
        if (tt==0)  printf("-1 ");
        else printf("%d ",a[tt]);
        a[++tt]=x; //新元素无论如何都要入栈
    }
    return 0;
}
单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些与数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个。 2. 从左到遍历数组元素。 3. 对于每个元素,将其与顶元素进行比较。 - 如果当前元素小于等于顶元素,则将当前元素入。 - 如果当前元素大于顶元素,则将顶元素弹出,并将当前元素入。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减。具体操作如下: 1. 创建一个一个空结果数组。 2. 从左到遍历数组元素。 3. 对于每个元素,将其与顶元素进行比较。 - 如果当前元素小于等于顶元素,则将当前元素入。 - 如果当前元素大于顶元素,则将顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值