每日算法刷题Day58:8.7:leetcode 单调栈5道题,用时2h
二.进阶
1.套路
2.题目描述
3.学习经验
1. 1019.链表中的下一个更大节点(中等)
1019. 链表中的下一个更大节点 - 力扣(LeetCode)
思想
1.给定一个长度为 n
的链表 head
对于列表中的每个节点,查找下一个 更大节点 的值。也就是说,对于每个节点,找到它旁边的第一个节点的值,这个节点的值 严格大于 它的值。
返回一个整数数组 answer
,其中 answer[i]
是第 i
个节点( 从1开始 )的下一个更大的节点的值。如果第 i
个节点没有下一个更大的节点,设置 answer[i] = 0
。
2.查找下一个更大节点的值就想到单调栈
代码
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:typedef pair<int, int> PII; // 下标-值vector<int> nextLargerNodes(ListNode* head) {int n = 0;ListNode* tmp = head;while (tmp) {++n;tmp = tmp->next;}vector<int> res(n, 0);vector<PII> stk;int id = 0;tmp = head;while (tmp) {while (!stk.empty() && tmp->val > stk.back().second) {res[stk.back().first] = tmp->val;stk.pop_back();}stk.push_back({id, tmp->val});++id;tmp = tmp->next;}return res;}
};
二.矩形
1.套路
1.可以枚举矩形的高,找左边和右边第一个比它小的下标left
和right
,用单调栈
2. 题目描述
3. 学习经验
1. 84. 柱状图中最大的矩形(困难,学习)
84. 柱状图中最大的矩形 - 力扣(LeetCode)
思想
1.给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
2.可以得出最大矩形的高肯定是数组里面的高,反证法证明即可。
这一点确定了,就可以枚举矩形的高,然后符合条件的矩形要求左右高度都大于等于它,那么就要找左边第一个比它小的下标left
.和右边第一个比它小的下标right
,那么宽度范围就是(left,right)
,宽度为right=left-1
,而找第一个比它小的下标就是单调栈,找两次即可。
因为左右是开区间,所以left
初值为-1,right
初值为n
代码
class Solution {
public:int largestRectangleArea(vector<int>& heights) {int n = heights.size();int res = 0;vector<int> left(n, -1);vector<int> right(n, n);vector<int> stk;for (int i = n - 1; i >= 0; --i) {while (!stk.empty() && heights[i] < heights[stk.back()]) {left[stk.back()] = i;stk.pop_back();}stk.push_back(i);}stk.clear();for (int i = 0; i < n; ++i) {while (!stk.empty() && heights[i] < heights[stk.back()]) {right[stk.back()] = i;stk.pop_back();}stk.push_back(i);}for (int i = 0; i < n; ++i) {// 宽为(left[i],right[i])res = max(res, heights[i] * (right[i] - left[i] - 1));}return res;}
};
三.贡献法
1.套路
2. 题目描述
3. 学习经验
1. 907. 子数组的最小值之和(中等,学习)
907. 子数组的最小值之和 - 力扣(LeetCode)
思想
1.给定一个整数数组 arr
,找到 min(b)
的总和,其中 b
的范围为 arr
的每个(连续)子数组。
由于答案可能很大,因此 返回答案模 10^9 + 7
。
2,此题和[[七.单调栈#1. 84. 柱状图中最大的矩形(困难,学习)]]一样,以枚举arr[i]
为视角,找它是最小值的范围,即左边第一个比它小的下标left
,和右边比它小的下标right
,然后求子数组,左边元素可以选(left,i]
.右边元素可以选[i,right)
,再按照乘法原理(i-left)*(right-i)
得到arr[i]
作为最小元素的子数组数量。
但是上面只能算无重复元素的,对于包含重复元素的,如下面例子:
arr=[1,2,4,2,3,1]
,若按上述算法,两个2的边界都是(0,5)
,会重复算,可以把一个边界变成第一个大于等于它的下标,以右边界为例,第一个2的边界是(0,3)
,第2个2的边界是(0,5)
,所以右边的重复元素把左边的重复元素的右边界给截断了,不会出现重复算
代码
四.最小字典序
1.套路
1.删除序列某些元素,保证序列相对顺序不变,使得剩下序列字典序最小。
即遇到后面元素,根据条件判断删除前面元素,用栈实现。
但是前提条件是未达到删除数量,所以要提前知道删除数量。
同时遍历完可能未删完,还要遍历删除数量再删除序列后面元素,保证删够数量。
class Solution {
public:vector<int> mostCompetitive(vector<int>& nums, int k) {int n = nums.size();// 删除数量int shan = n - k;vector<int> stk;for (int i = 0; i < n; ++i) {// 未删完且要删while (!stk.empty() && shan > 0 && stk.back() > nums[i]) {stk.pop_back();--shan;}stk.push_back(nums[i]);}// 未删完while (shan) {stk.pop_back();--shan;}return stk;}
};
2. 题目描述
3. 学习经验
1. 402. 移除K位数字(中等,学习)
402. 移掉 K 位数字 - 力扣(LeetCode)
思想
1.给你一个以字符串表示的非负整数 num
和一个整数 k
,移除这个数中的 k
位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
2.这题首先利用贪心思想,若还可以移除元素,且遍历到的数字比前一个数字小,则可以把前一个数字出栈,则先进后出,利用栈实现,则上述逻辑可以保证在一段区间内单调递增。但是会有以下问题:
- 未删除
k
个元素,则还要再遍历k
,将栈中末尾元素弹出 - 若当前栈前面是前导0,则把栈中元素转移到答案中,要先保证没有前导0
- 答案为空串,对应数字为0
代码
class Solution {
public:string removeKdigits(string num, int k) {int n = num.size();vector<char> stk;int len = n - k;for (int i = 0; i < n; ++i) {while (!stk.empty() && num[i] < stk.back() && k > 0) {stk.pop_back();--k;}stk.push_back(num[i]);}for (; k > 0; --k)stk.pop_back();bool isLeadingZero = true;string res = "";for (auto& c : stk) {if (isLeadingZero && c == '0')continue;isLeadingZero = false;res += c;}return res == "" ? "0" : res;}
};
2. 1673. 找出最具竞争力的子序列(中等)
1673. 找出最具竞争力的子序列 - 力扣(LeetCode)
思想
1.给你一个整数数组 nums
和一个正整数 k
,返回长度为 k
且最具 竞争力 的 nums
子序列。
数组的子序列是从数组中删除一些元素(可能不删除元素)得到的序列。
在子序列 a
和子序列 b
第一个不相同的位置上,如果 a
中的数字小于 b
中对应的数字,那么我们称子序列 a
比子序列 b
(相同长度下)更具 竞争力 。 例如,[1,3,4]
比 [1,3,5]
更具竞争力,在第一个不相同的位置,也就是最后一个位置上, 4
小于 5
。
2.依旧是判断当前遍历元素小于前一元素时,删去前一元素,用栈实现。但删的前提是没达到删除上限,这题得先算一下删除数量。
依旧遍历完可能没删完,再遍历删除数量保证删完。
代码
class Solution {
public:vector<int> mostCompetitive(vector<int>& nums, int k) {int n = nums.size();int shan = n - k;vector<int> stk;for (int i = 0; i < n; ++i) {while (!stk.empty() && shan > 0 && stk.back() > nums[i]) {stk.pop_back();--shan;}stk.push_back(nums[i]);}while (shan) {stk.pop_back();--shan;}return stk;}
};