1 专题说明
本博客用来计算力扣上的单调栈题目、解题思路和代码。
单调栈题目记录:
- 2866美丽塔II
- 496下一个更大元素I
- 503下一个更大元素II
- 2454下一个更大元素IV
- 456题132模式
- 739每日温度
- 901股票价格跨度
- 1019链表中的下一个更大节点
- 1124表现良好的最长时间段
- 1475商品折扣后的最终价格
- 2289使数组按非递减顺序排列
- 1944队列中可以看到的人数
2 训练
题目1:2866美丽塔II。
解题思路:先计算出prefix[i],表示0~i
满足递增情况下,0~i
上的元素之和最大值。然后计算出suffix[i],表示i~n-1
满足递增情况下,i~n-1
上的元素之和最大值。那么以i
为峰顶的美丽塔的元素之和的最大值为prefix[i] + suffix[i] - nums[i]
,遍历i,获得答案即可。
本质上,还是可以归类为:找到i左边,并且<=nums[i]的元素值。
C++代码如下,
class Solution {
public:
long long maximumSumOfHeights(vector<int>& maxHeights) {
int n = maxHeights.size();
vector<long long> prefix(n, 0); //prefix[i]表示0~i是递增的情况下,0~i的元素之和
stack<int> stk;
for (int i = 0; i < n; ++i) {
while (!stk.empty() && maxHeights[stk.top()] > maxHeights[i]) {
stk.pop();
}
if (stk.empty()) {
prefix[i] = (long long)(i + 1) * maxHeights[i];
} else {
prefix[i] = prefix[stk.top()] + (long long)(i - stk.top()) * maxHeights[i];
}
stk.push(i);
}
while (!stk.empty()) {
stk.pop();
}
vector<long long> suffix(n, 0); //suffix[i]表示i~n-1是递减的情况下,i~n-1的元素之和
for (int i = n - 1; i >= 0; --i) {
while (!stk.empty() && maxHeights[stk.top()] > maxHeights[i]) {
stk.pop();
}
if (stk.empty()) {
suffix[i] = (long long)(n - i) * maxHeights[i];
} else {
suffix[i] = suffix[stk.top()] + (long long)(stk.top() - i) * maxHeights[i];
}
stk.push(i);
}
long long res = 0;
for (int i = 0; i < n; ++i) {
res = max(res, prefix[i] + suffix[i] - maxHeights[i]);
}
return res;
}
};
python3代码如下,
class Solution:
def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
n = len(maxHeights)
prefix = [0 for i in range(n)] #0~i的递增数组的和的最大值
stk = []
for i in range(n):
while len(stk) and maxHeights[stk[-1]] > maxHeights[i]:
del stk[-1]
if len(stk) == 0:
prefix[i] = (i + 1) * maxHeights[i]
else:
prefix[i] = prefix[stk[-1]] + (i - stk[-1]) * maxHeights[i]
stk.append(i)
stk.clear()
suffix = [0 for i in range(n)] #i~n-1的递减数组的和的最大值
for i in range(n-1,-1,-1):
while len(stk) and maxHeights[stk[-1]] > maxHeights[i]:
del stk[-1]
if len(stk) == 0:
suffix[i] = (n - i) * maxHeights[i]
else:
suffix[i] = suffix[stk[-1]] + (stk[-1] - i) * maxHeights[i]
stk.append(i)
res = 0
for i in range(n):
#print(f"i = {i}, prefix[i] = {prefix[i]}, suffix[i] = {suffix[i]}.")
res = max(res, prefix[i] + suffix[i] - maxHeights[i])
return res
题目2:496下一个更大元素I。
解题思路:直接找右边首次大于它的元素即可。
C++代码如下,
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int,int> mp; //mp[x]表示nums2中元素x的右边,第一个比它大的元素
stack<int> stk;
for (int i = nums2.size() - 1; i >= 0; --i) {
while (!stk.empty() && stk.top() <= nums2[i]) {
stk.pop();
}
if (!stk.empty()) {
mp[nums2[i]] = stk.top();
} else {
mp[nums2[i]] = -1;
}
stk.push(nums2[i]);
}
vector<int> res;
for (auto x : nums1) {
res.emplace_back(mp[x]);
}
return res;
}
};
python3代码如下,
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
n = len(nums2)
mp = collections.defaultdict(int)
stk = []
for i in range(n - 1, -1, -1):
while len(stk) and stk[-1] <= nums2[i]:
del stk[-1]
if len(stk):
mp[nums2[i]] = stk[-1]
else:
mp[nums2[i]] = -1
stk.append(nums2[i])
res = []
for x in nums1:
res.append(mp[x])
return res
题目3:503下一个更大元素II。
解题思路:环形问题,扩展两倍原数组即可,接下来就是找右侧首次大于它的元素。
C++代码如下,
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n = nums.size();
vector<int> a(2 * n, 0);
for (int i = 0; i < n; ++i) {
a[i] = a[i + n] = nums[i];
}
vector<int> ans(2 * n, -1);
stack<int> stk;
for (int i = 2 * n - 1; i >= 0; --i) {
while (!stk.empty() && stk.top() <= a[i]) {
stk.pop();
}
if (!stk.empty()) {
ans[i] = stk.top();
}
stk.push(a[i]);
}
vector<int> res(n, -1);
for (int i = 0; i < n; ++i) {
res[i] = ans[i];
}
return res;
}
};
python3代码如下,
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
n = len(nums)
a = [-1 for i in range(2 * n)]
for i in range(n):
a[i] = a[i + n] = nums[i]
ans = [-1 for i in range(2 * n)]
stk = []
for i in range(2 * n - 1, -1, -1):
while len(stk) and stk[-1] <= a[i]:
del stk[-1]
if len(stk):
ans[i] = stk[-1]
stk.append(a[i])
res = [-1 for i in range(n)]
for i in range(n):
res[i] = ans[i]
return res
题目4:2454下一个更大元素IV。
解题思路:比较难,不懂先放一边。
题目5:456题132模式。
解题思路:找到右边第一个大于等于它的元素,记录弹出栈的元素的最大值k
,如果k > nums[i]
,返回true
。这思路比较难想到。
C++代码如下,
class Solution {
public:
bool find132pattern(vector<int>& nums) {
//找到右边第1个大于等于它的数
int n = nums.size();
stack<int> stk;
int k = INT_MIN;
for (int i = n - 1; i >= 0; --i) {
if (k > nums[i]) return true;
while (!stk.empty() && stk.top() < nums[i]) {
k = max(k, stk.top());
stk.pop();
}
stk.push(nums[i]);
}
return false;
}
};
python3代码如下,
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
#找到右边第一个大于等于它的元素
n = len(nums)
stk = []
k = -1e9
for i in range(n - 1, -1, -1):
if k > nums[i]:
return True
while len(stk) and stk[-1] < nums[i]:
k = max(k, stk[-1])
del stk[-1]
stk.append(nums[i])
return False
题目6:739每日温度。
解题思路:找到右边比它大的数的下标。
C++代码如下,
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
//找到右边比它大数的下标
int n = temperatures.size();
vector<int> res(n, 0);
stack<int> stk;
for (int i = n - 1; i >= 0; --i) {
while (!stk.empty() && temperatures[stk.top()] <= temperatures[i]) {
stk.pop();
}
if (!stk.empty()) {
res[i] = stk.top() - i;
}
stk.push(i);
}
return res;
}
};
python3代码如下,
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
#找到右边比它大的数的下标
n = len(temperatures)
res = [0 for i in range(n)]
stk = []
for i in range(n - 1, -1, -1):
while len(stk) and temperatures[stk[-1]] <= temperatures[i]:
del stk[-1]
if len(stk):
res[i] = stk[-1] - i
stk.append(i)
return res
题目7:901股票价格跨度。
解题思路:左边大于它的数的下标。
C++代码如下,
class StockSpanner {
public:
StockSpanner() {
//左边大于它这个数的下标
i = 0;
while (!stk.empty()) {
stk.pop();
}
return;
}
int next(int price) {
int res = 1;
while (!stk.empty() && stk.top().second <= price) {
stk.pop();
}
if (!stk.empty()) {
res = i - stk.top().first;
} else {
res = i + 1;
}
stk.push(make_pair(i, price));
i += 1;
return res;
}
private:
int i = 0;
stack<pair<int,int>> stk;
};
python3代码如下,
class StockSpanner:
def __init__(self):
self.i = 0
self.stk = []
return
def next(self, price: int) -> int:
res = 1
while len(self.stk) and self.stk[-1][1] <= price:
del self.stk[-1]
if len(self.stk):
res = self.i - self.stk[-1][0]
else:
res = self.i + 1
self.stk.append([self.i, price])
self.i += 1
return res
题目8:1019链表中的下一个更大节点。
解题思路:找到右边第一个比它大的数。
C++代码如下,
/**
* 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:
vector<int> nextLargerNodes(ListNode* head) {
//右边第一个比它大的值
vector<int> nums;
ListNode *node = head;
while (node != nullptr) {
nums.emplace_back(node->val);
node = node->next;
}
int n = nums.size();
vector<int> res(n, 0);
stack<int> stk;
for (int i = n - 1; i >= 0; --i) {
while (!stk.empty() && stk.top() <= nums[i]) {
stk.pop();
}
if (!stk.empty()) {
res[i] = stk.top();
}
stk.push(nums[i]);
}
return res;
}
};
python3代码如下,
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def nextLargerNodes(self, head: Optional[ListNode]) -> List[int]:
nums = []
node = head
while node is not None:
nums.append(node.val)
node = node.next
n = len(nums)
res = [0 for i in range(n)]
stk = []
for i in range(n-1,-1,-1):
while len(stk) and stk[-1] <= nums[i]:
del stk[-1]
if len(stk):
res[i] = stk[-1]
stk.append(nums[i])
return res
题目9:1124表现良好的最长时间段。
解题思路:暂时没有理解,先跳过。
题目10:1475商品折扣后的最终价格。
解题思路:右边第一个小于等于它的数。
C++代码如下,
class Solution {
public:
vector<int> finalPrices(vector<int>& prices) {
//下一个小于等于它的数
int n = prices.size();
vector<int> res = prices;
stack<int> stk;
for (int i = n - 1; i >= 0; --i) {
while (!stk.empty() && stk.top() > prices[i]) {
stk.pop();
}
if (!stk.empty()) {
res[i] -= stk.top();
}
stk.push(prices[i]);
}
return res;
}
};
python3代码如下,
class Solution:
def finalPrices(self, prices: List[int]) -> List[int]:
#右边第一个小于等于它的数
res = copy.deepcopy(prices)
n = len(res)
stk = []
for i in range(n-1,-1,-1):
while len(stk) and stk[-1] > prices[i]:
del stk[-1]
if len(stk):
res[i] -= stk[-1]
stk.append(prices[i])
return res
题目11:2289使数组按非递减顺序排列。
解题思路:还未仔细研究。
题目12:1944队列中可以看到的人数。
解题思路:手推性质。