[leetcode] 单调栈
本文总结单调栈算法。
原问题
学习一个算法,我们需要清楚的是:这个算法最原始的问题背景是什么样的?
下一个更小元素
给定一个数组 nums,返回每个元素的下一个更小的元素的下标 res,即 res[i] 记录的是 nums[i] 右端第一个比它小的元素的下标(不存在则为 -1 )。
例如 nums = [2,1,2,4,3],那么 res = [1, -1, -1, 4, -1] .
从左往右扫描数组,栈底到栈顶维持严格升序,当扫描当前元素 nums[i] = x 时,如果需要出栈(说明栈顶大于等于当前的 x ),那么 x 就是出栈元素的下一个更小元素。
vector<int> nextSmallerNumber(vector<int> &&nums)
{
int n = nums.size(), idx = -1;
vector<int> res(n, -1);
stack<int> stk;
for (int i = 0; i < n; i++)
{
while (!stk.empty() && nums[i] <= nums[stk.top()])
{
idx = stk.top(), stk.pop();
res[idx] = i;
}
stk.emplace(i);
}
return res;
}
相关题目:
下一个更大元素
给定一个数组 nums,返回每个元素的下一个更大的元素的下标 res,即 res[i] 记录的是 nums[i] 右端第一个比它大的元素的下标(不存在则为 -1 )。
例如 nums = [2,1,2,4,3],那么 res = [3, 2, 3, -1, -1] .
从左往右扫描数组,栈底到栈顶维持降序(不要求严格),当扫描当前元素 nums[i] = x 时,如果需要出栈(说明栈顶严格小于当前的 x ),那么 x 就是出栈元素的下一个更大元素。
vector<int> nextGreaterNumber(vector<int> &&nums)
{
int n = nums.size(), idx;
vector<int> res(n, -1);
stack<int> stk;
for (int i = 0; i < n; i++)
{
while (!stk.empty() && nums[stk.top()] < nums[i])
{
idx = stk.top(), stk.pop();
res[idx] = i;
}
stk.emplace(i);
}
return res;
}
类似题目:
Leetcode
下一个更大元素 I
题目保证 nums1 是 nums2 的子集,首先在 nums2 先做一次「下一个更大」元素,使用一个哈希表记录结果。
然后扫描 nums1 ,把哈希表的结果按序填入数组 res 即可。
每次自己写出了最优解,并且官方也是同一思路,都会觉得好有成就感 。
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2)
{
unordered_map<int,int> table;
// 单调递减栈,不需要严格递减
stack<int> stk;
for (int x: nums2)
{
while (!stk.empty() && stk.top() < x)
{
table[stk.top()] = x;
stk.pop();
}
stk.emplace(x);
}
int n = nums1.size();
vector<int> res(n, -1);
for (int i=0; i<n; i++)
{
if (table.count(nums1[i]))
res[i] = table[nums1[i]];
}
return res;
}
};
下一个更大元素 II
这里数组是一个 循环数组 ,那么最简单的处理方式当然就是令 nums = nums + nums 了,这样做完一遍「下一个更大元素」之后,只需要截取 res 数组的前一半即可。
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
if (nums.size() == 0) return {};
nums.insert(nums.end(), nums.begin(), nums.end());
int n = nums.size(), idx;
vector<int> res(n, -1);
stack<int> stk; // 单调递减栈,不需要严格递减
for (int i=0; i<n; i++)
{
while (!stk.empty() && nums[stk.top()] < nums[i])
{
idx = stk.top(), stk.pop();
res[idx] = nums[i];
}
stk.push(i);
}
return vector<int>(res.begin(), res.begin() + n/2);
}
};
那么,有时候,面试官就对最优解非常苛刻(比如微软),不允许我们使用这种额外空间,那么就要使用取模的方式去模拟循环数组:
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
if (nums.size() == 0) return {};
int n = nums.size(), idx;
vector<int> res(n, -1);
stack<int> stk; // 单调递减栈,不需要严格递减
for (int i=0; i<=2*n-1; i++)
{
while (!stk.empty() && nums[stk.top()] < nums[i % n])
{
idx = stk.top(), stk.pop();
res[idx] = nums[i % n];
}
stk.push(i % n);
}
return res;
}
};
结果模运算多了,时间效率还不如第一种。
每日温度
题目:739. 每日温度
本题就是「下一个更大元素」的裸题了,维持一个递减栈(记录下标)即可。
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& T) {
int n = T.size(), idx = 0;
vector<int> res(n, 0);
stack<int> stk; // 单调递减栈
for (int i=0; i<n; i++)
{
while (!stk.empty() && T[stk.top()] < T[i])
{
idx = stk.top(), stk.pop();
res[idx] = i - idx;
}
stk.emplace(i);
}
return res;
}
};
其他
两侧的更小值 I
题目:两侧的更小值
微软的面试题 ,这是套「下一个更小元素」的模版。此处不含重复元素
维持一个严格升序的栈,当扫描当前元素 nums[i] = x 时,如果需要出栈(说明栈顶大于等于当前的 x ),那么 x 就是出栈元素的右侧更小值。那么,出栈元素的左侧更小值在哪呢?就是它在栈中的邻居。
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
vector<pair<int, int>> solve(vector<int> &nums)
{
int n = nums.size(), idx;
stack<int> stk; // 严格递增栈
vector<pair<int, int>> res(n, {-1, -1});
for (int i = 0; i < n; i++)
{
while (!stk.empty() && nums[stk.top()] >= nums[i])
{
idx = stk.top(), stk.pop();
res[idx].second = i;
res[idx].first = (stk.empty() ? -1 : stk.top());
}
stk.push(i);
}
while (!stk.empty())
{
idx = stk.top(), stk.pop();
res[idx].first = (stk.empty() ? -1 : stk.top());
}
return res;
}
int main()
{
int n;
cin >> n;
vector<int> nums(n, 0);
for (int i = 0; i < n; i++) cin >> nums[i];
auto ans = solve(nums);
for (auto [x,y]: ans) printf("%d %d\n", x, y);
}
两侧的更小值 II
题目:两侧的更小值 II
此处含有重复元素。
那么我们还是维持一个递增的栈(不要求严格),当扫描 nums[i] 时需要出栈,说明 nums[s.top()] 严格大于 nums[i],那么就找到了 nums[s.top()] 的右侧更小值是 nums[i] 。
那么 nums[s.top()] 左侧更小值在哪呢?是否就是在栈中的邻居呢?答案是否定的。
比如输入:[1, 3, 3, 1] . 当扫描到最后一个元素 1 的时候:
stk: 1 3 3 (1)
^ ^
| |
left cur
这时候显然需要出栈,那么两个 3 的右侧更小值都是 cur ,但栈顶的 3 的左侧更小值不是它的邻居(而是 left 指向的 1 )。
这时候,我们用一个 buf 把这样 3 都记录下来,那么 buf 中的元素,它们的两侧更小值都是 {left, cur} 。如果 left 不存在(栈为空),那么 left = -1 。
注意:代码实现中,栈存放的是下标。
代码实现
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
vector<pair<int, int>> solve(vector<int> &nums)
{
int n = nums.size(), idx;
stack<int> stk; // 递增栈,不要求严格
vector<pair<int, int>> res(n, {-1, -1});
for (int i = 0; i < n; i++)
{
while (!stk.empty() && nums[stk.top()] > nums[i])
{
idx = stk.top(), stk.top();
vector<int> buf = {idx};
while (!stk.empty() && nums[stk.top()] == nums[idx])
buf.emplace_back(stk.top()), stk.pop();
for (int x : buf)
{
res[x].first = (stk.empty() ? -1 : stk.top());
res[x].second = i;
}
}
stk.emplace(i);
}
while (!stk.empty())
{
idx = stk.top(), stk.top();
vector<int> buf = {idx};
while (!stk.empty() && nums[stk.top()] == nums[idx])
buf.emplace_back(stk.top()), stk.pop();
for (int x : buf)
res[x].first = (stk.empty() ? -1 : stk.top());
}
return res;
}
[leetcode] 单调栈的更多相关文章
- leetcode Maximal Rectangle 单调栈
作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4052721.html 题目链接:leetcode Maximal Rectangle 单调栈 ...
- leetcode Largest Rectangle in Histogram 单调栈
作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4052343.html 题目链接 leetcode Largest Rectangle in ...
- LeetCode Monotone Stack Summary 单调栈小结
话说博主在写Max Chunks To Make Sorted II这篇帖子的解法四时,写到使用单调栈Monotone Stack的解法时,突然脑中触电一般,想起了之前曾经在此贴LeetCode Al ...
- LeetCode 84. Largest Rectangle in Histogram 单调栈应用
LeetCode 84. Largest Rectangle in Histogram 单调栈应用 leetcode+ 循环数组,求右边第一个大的数字 求一个数组中右边第一个比他大的数(单调栈 Lee ...
- LeetCode 84 | 单调栈解决最大矩形问题
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题第52篇文章,我们一起来看LeetCode第84题,Largest Rectangle in Histogram( ...
- [LeetCode]739. 每日温度(单调栈)
题目 根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数.如果之后都不会升高,请在该位置用 0 来代替. 例如,给定一个列表 temperatures ...
- [每日一题2020.06.13]leetcode #739 #15 单调栈 双指针查找
739 每日温度 ( 单调栈 ) 题目 : https://leetcode-cn.com/problems/daily-temperatures/ 题意 : 找到数组每一个元素之后第一个大于它的元素 ...
- leetcode 321. 拼接最大数(单调栈,分治,贪心)
题目链接 https://leetcode-cn.com/problems/create-maximum-number/ 思路: 心都写碎了.... 也许就是不适合吧.... 你是个好人... cla ...
- 【leetcode】85. Maximal Rectangle(单调栈)
Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing onl ...
随机推荐
- http cache & 浏览器缓存,存储位置的优先级,条件?
http cache & 浏览器缓存,存储位置的优先级,条件? memory cache disk cache 浏览器缓存,存储位置的优先级,条件, 机制,原理是什么? from memory ...
- back to top & back to bottom
back to top & back to bottom infinite auto load more & infinite scroll & load more https ...
- windows driver 简单的驱动和通信
sysmain.c #pragma once #pragma warning(disable: 4100) #include <ntifs.h> #include <ntddk.h& ...
- Flutter 设置input边框
example 1 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp ext ...
- spring扩展点整理
本文转载自spring扩展点整理 背景 Spring的强大和灵活性不用再强调了.而灵活性就是通过一系列的扩展点来实现的,这些扩展点给应用程序提供了参与Spring容器创建的过程,好多定制化的东西都需要 ...
- 一条sql语句的执行过程
一条select语句执行流程 第一步:连接器 连接器负责跟客户端建立连接.获取权限.维持和管理连接.如果用户名密码验证通过后,连接器会到权限表里面查出你拥有的权限.之后该连接的权限验证都依赖于刚查出来 ...
- C#日志使用
本文参考链接 日志框架 框架选择:NLog 安装方法,Nuget命令行:Install-Package NLog 常用规则 尽量不要在循环中打印日志. 应输出错误的堆栈信息:e.Message仅为异常 ...
- 2.5w字 + 36 张图爆肝操作系统面试题,太牛逼了!
欢迎各位大佬访问我的 github ,跪求 star bestJavaer 大家好,我是 cxuan,我之前汇总了一下关于操作系统的面试题,最近又重新翻阅了一下发现不是很全,现在也到了面试季了,所以我 ...
- vue3中使用axios如何去请求数据
在vue2中一般放在created中,但是在vue3中取消了created生命周期,请求方式有两种 直接在setup中去获取数据 setup(props) { const data = reactiv ...
- PVE更新WEB管理地址
PVE也是一台Linux系统,如果PVE更换了网络环境,比如从家里拿到了办公室,那么就需要对其更新网络,才能让其它机器访问到它的8006管理地址. 具体做法是通过修改配置文件来更改IP. 更新网卡配置 ...