力扣单调栈算法专题训练

1 专题说明

本博客用来计算力扣上的单调栈题目、解题思路和代码。

单调栈题目记录:

  1. 2866美丽塔II
  2. 496下一个更大元素I
  3. 503下一个更大元素II
  4. 2454下一个更大元素IV
  5. 456题132模式
  6. 739每日温度
  7. 901股票价格跨度
  8. 1019链表中的下一个更大节点
  9. 1124表现良好的最长时间段
  10. 1475商品折扣后的最终价格
  11. 2289使数组按非递减顺序排列
  12. 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队列中可以看到的人数。

解题思路:手推性质

3 参考

acwing算法基础之数据结构–栈、单调栈、队列和单调队列

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YMWM_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值