每日算法刷题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.可以枚举矩形的高,找左边和右边 第一个比它小的下标leftright,用单调栈

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;
    }
};
相关推荐
发发发发8881 小时前
leetcode 415.字符串相加
linux·服务器·leetcode
Vesan,2 小时前
无人机开发分享——基于行为树的无人机集群机载自主决策算法框架搭建及开发
c++·算法·决策树·无人机
Monika Zhang3 小时前
【面试攻略】回答Java面试问题「挑战与解决方案」技巧
面试·职场和发展
董莉影3 小时前
学习嵌入式第二十二天
数据结构·学习·算法·链表
Lukeding4 小时前
Magnetic-UI源码解析
算法
chirrupy_hamal4 小时前
排序算法详解
算法
BOB_BOB_BOB_4 小时前
【ee类保研面试】数学类---概率论
面试·职场和发展·概率论·保研
csdn_aspnet4 小时前
四边形面积
算法·四边形
爱coding的橙子4 小时前
每日算法刷题Day57:8.6:leetcode 单调栈6道题,用时2h
算法·leetcode·职场和发展