二.进阶
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. 柱状图中最大的矩形(困难,学习)
思想
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. 子数组的最小值之和(中等,学习)
思想
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位数字(中等,学习)
思想
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;
}
};