c++算法学习笔记 (15) 单调栈与单调队列

简介: c++算法学习笔记 (15) 单调栈与单调队列

核心思想是先暴力想一遍,然后找没有用到的元素并删掉看剩余元素有无单调性。若有,就用单调栈/单调队列/二分来优化

1.单调栈

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

输入格式

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

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

输出格式

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

数据范围

1≤N≤10^5

1≤数列中元素≤10^9

输入样例:
5
3 4 2 7 5


输出样例:
-1 3 -1 2 2


代码:这里往右遍历,如果遇到一个更小的数,说明之前存的数全部没有用了,所以全部弹出栈。

#include <iostream>
using namespace std;
const int N = 100010;
int n;
int stk[N], tt; // i左边的数存入stack,且满足单增栈
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        int x;
        cin >> x;
        while (tt && stk[tt] >= x) 
        // 栈非空&&栈顶元素>=x  栈内遇见一个>=x的数就弹出,保证插入x后仍为单增栈
            tt--;
        if (tt) // 非空
            cout << stk[tt] << " ";
        else // 空
            cout << -1 << " ";
        stk[++tt] = x;
    }
 
    return 0;
}


2.单调队列

滑动窗口

给定一个大小为 n≤10^6 的数组。

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

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

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

以下是一个例子:

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

窗口位置 最小值 最大值
[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


代码:无用元素1:不在窗口的元素;无用元素2:在窗口中最小值>要进来的元素,之前的最小值删去,保留新的最小值。

#include <iostream>
using namespace std;
const int N = 1000010;
int n, k;
int a[N], q[N]; // q[]存元素下标
int main()
{
    scanf("%d %d", &n, &k);
    int hh = 0, tt = -1;
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
    }
    for (int i = 0; i < n; i++) // 输出最小元素
    {
        // 判断队头是否已经溢出窗口
        if (hh <= tt && i - k + 1 > q[hh]) // 不用while因为每次窗口只滑动一格
        {
            hh++;
        }
        while (hh <= tt && a[q[tt]] > a[i])
        { // 如果要进来的元素<队尾元素
            tt--;
        }
        q[++tt] = i; // 这里的位置不能变
        if (i >= k - 1)
        {
            printf("%d ", a[q[hh]]);
        }
    }
    cout << endl;
    hh = 0, tt = -1;//记得要重置!!!!
    for (int i = 0; i < n; i++) // 输出最大元素
    {
        // 判断队头是否已经溢出窗口
        if (hh <= tt && i - k + 1 > q[hh]) // 不用while因为每次窗口只滑动一格
        {
            hh++;
        }
        while (hh <= tt && a[q[tt]] < a[i])
        { // 如果要进来的元素>队尾元素
            tt--;
        }
        q[++tt] = i; // 这里的位置不能变
        if (i >= k - 1)
        {
            printf("%d ", a[q[hh]]);
        }
    }
    return 0;
}


相关文章
|
15小时前
|
程序员 编译器 C语言
【C++】学习笔记——C++入门_1
【C++】学习笔记——C++入门_1
9 4
|
1天前
|
算法
【C/数据结构和算法】:栈和队列
【C/数据结构和算法】:栈和队列
6 1
|
1天前
|
算法 前端开发 安全
C++算法模板
C++算法模板
3 0
|
4天前
|
算法 数据处理 C++
C++一分钟之-迭代器与算法
【6月更文挑战第21天】C++ STL的迭代器统一了容器元素访问,分为多种类型,如输入、输出、前向、双向和随机访问。迭代器使用时需留意失效和类型匹配。STL算法如查找、排序、复制要求特定类型的迭代器,注意容器兼容性和返回值处理。适配器和算法组合增强灵活性,但过度使用可能降低代码可读性。掌握迭代器和算法能提升编程效率和代码质量。
22 3
|
5天前
|
算法 Java 编译器
Java数据结构与算法:线性数据结构之栈
Java数据结构与算法:线性数据结构之栈
|
5天前
|
机器学习/深度学习 算法 BI
机器学习笔记(一) 感知机算法 之 原理篇
机器学习笔记(一) 感知机算法 之 原理篇
|
9天前
|
算法 前端开发 Linux
【常用技巧】C++ STL容器操作:6种常用场景算法
STL在Linux C++中使用的非常普遍,掌握并合适的使用各种容器至关重要!
33 10
|
11天前
|
存储 算法 C++
【数据结构与算法】:带你手搓顺序表(C/C++篇)
【数据结构与算法】:带你手搓顺序表(C/C++篇)
|
11天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
11天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题