代码随想录 | Day 59 - LeetCode 503. 下一个更大元素II、LeetCode 42. 接雨水
admin
2024-02-01 05:18:09
0

        今天是单调栈的第2天,第1道题是前面的延续,第2道题很难还常考。第2题双指针和DP解法重点是“当前位置的雨水量取决于左右两边柱子最高高度”,单调栈解法则要熟悉“左、中、右三个柱子各自的含义和作用”。


        第1题(LeetCode 503. 下一个更大元素II)相比day 58中第1题(LeetCode 739. 每日温度)变成了循环数组,且要求返回的结果是更大的数字本身,而不是下标的差值。对于循环数组,某个值更大的元素可能出现在其左边部分,最极端的情况是某个元素的更大值出现在与其相邻的左边。所以要遍历两次nums才能保证所有元素对应的值更大元素在其之后被遍历到。

        具体的做法有两种,第1种是将nums复制一份拼接在其后(代码如下),再用 day 58中第1题(LeetCode 739. 每日温度)中的方法遍历该数组。

vector nums1(nums.begin(), nums.end());
nums.insert(nums.end(), nums1.begin(), nums1.end());

        第2种方法则是不占用额外空间,用下标取余数的方式来模拟两次遍历数组。这种方法更高效。

class Solution {
public:vector nextGreaterElements(vector& nums) {vector res(nums.size(), -1);stack st;st.push(0);for (int i = 0; i < 2 * nums.size(); ++i) {int ind = i % nums.size();if (nums[ind] <= nums[st.top()]) {st.push(ind);}else {while (!st.empty() && nums[ind] > nums[st.top()]) {res[st.top()] = nums[ind];st.pop();}st.push(ind);}}return res;}
};

        第2题(LeetCode 42. 接雨水)自己想了很久还是没想出,是一道高频考题需要注意。题解中有三种解法,分别是单调栈、双指针、DP解法。

        单调栈是三种方法中最难的一种,核心思想是维持单调递减的栈,在出现非递减元素时,弹出栈中元素的同时加雨水,每次所加的雨水数量都是其宽度和高度的乘积。当前元素与栈顶元素有小于、等于和大于3种情况:

  1. 小于:为了维护单调递减关系,在遇到比栈顶元素更小值时将其下标入栈;
  2. 等于:两者相等时应该将旧的元素出栈,更新为新元素下标,便于第3种情况中左柱子位置的确定。
  3. 大于:将当前非递减元素、栈顶元素、栈顶元素的下一个元素分别定义为右、中、左柱子,那么当前需要加的雨水数量就由这三个柱子决定。当前与水的宽度就是(右柱子下标 - 左柱子下标 - 1)。减1是因为只计算中间凹陷处的雨水,不包含边缘。而高度需要取左、右柱子的较小值与中间柱子高度的差值。宽度与高度相乘就得到需要加的雨水数量。比如左、中、右柱子高度分别为[1, 0, 2],下标分别为[0, 2, 3](像下标1这样的情况是因为之前就被当做中间柱子而出栈),宽度就是(3 - 0 - 1) = 2,高度是(min(1, 2) - 0) = 1,所以雨水量是1 * 2 = 2。

        第3种情况的实现较为复杂,需要首先得到中间柱子,然后使其出栈,再得到左柱子,然后再计算宽度、高度,加雨水量。加完之后,有可能此时的栈顶元素,也就是左柱子,仍然小于当前非递减元素。所以要继续将其作为中柱子来重复上面的过程,所以这一过程要用while()来进行,其中的循环条件要首先保证栈非空,然后就是栈顶元素小于当前非递减元素。

// 单调栈
class Solution {
public:int trap(vector& height) {int ans = 0;stack st;st.push(0);for (int i = 1; i < height.size(); ++i) {if (height[i] < height[st.top()]) {st.push(i);}else if (height[i] == height[st.top()]) {st.pop();st.push(i);}else {while (!st.empty() && height[i] > height[st.top()]) {int mid = st.top();st.pop();if (!st.empty()) {int w = i - st.top() - 1;int h = min(height[i], height[st.top()]) - height[mid];ans += w * h;}}st.push(i);}}return ans;}
};

        双指针解法相对容易理解,但时间复杂度为O(n²),较高,会超时。这里采用按列计算的方法,也就是单独计算每个位置的雨水高度。这个高度取决于当前位置左边部分的最高柱子、和右边部分最高柱子,两者中较小值,再减去当前位置的柱子高度,就是当前位置的雨水高度。所以需要在每个位置分别向左、右遍历,计算左、右两边的最高柱子高度。在遍历之前,可以将最高柱子高度初始化为当前柱子高度,这样一来,如果左边或右边没有更高的柱子,最后当前位置的雨水量就是0。最后需要注意最左边和最右边位置是不会接到雨水的,所以循环范围应该不包括左、右两端点。

// 双指针法,超时
class Solution {
public:int trap(vector& height) {int ans = 0;for (int i = 1; i < height.size() - 1; ++i) {int leftMax = height[i], rightMax = height[i];for (int j = i - 1; j >= 0; --j) {leftMax = max(leftMax, height[j]);}for (int j = i + 1; j < height.size(); ++j) {rightMax = max(rightMax, height[j]);}ans += min(leftMax, rightMax) - height[i];}return ans;}
};

        DP解法沿用了上面双指针方法的思路,只不过会用DP方法提前一次性计算出每个位置左、右两边的最高柱子高度。dpLeft[i]、dpRight[i]分别定义为位置i左、右两边的最高柱子高度。对于位置i,其左边部分最高柱子高度要么是其左邻居的左边部分最高柱子高度,要么是自己本身,对应max(dp[i - 1], height[i]);同理其右边部分最高柱子高度对应max(dp[i + 1], height[i])。初始化部分,dpLeft最左端、dpRight最右端需要分别初始化为height的最左端、最右端值。其他部分与双指针法一致。

// DP
class Solution {
public:int trap(vector& height) {vector dpLeft(height.size()), dpRight(height.size());dpLeft[0] = height[0];dpRight[height.size() - 1] = height[height.size() - 1];for (int i = 1; i < height.size(); ++i) {dpLeft[i] = max(dpLeft[i - 1], height[i]);}for (int i = height.size() - 2; i >= 0; --i) {dpRight[i] = max(dpRight[i + 1], height[i]);}int ans = 0;for (int i = 1; i < height.size() - 1; ++i) {ans += min(dpLeft[i], dpRight[i]) - height[i];}return ans;}
};

相关内容

热门资讯

制作安卓系统主题软件,安卓系统... 你有没有想过,给你的安卓手机换一个全新的面貌?没错,就是那种一打开手机,就能感受到完全不同的风格和氛...
安卓系统平板怎么截屏,操作指南... 亲爱的平板用户,你是不是也和我一样,有时候想记录下平板上的精彩瞬间,却发现截屏功能有点神秘?别担心,...
安卓系统不推送更新,揭秘背后的... 最近是不是发现你的安卓手机有点儿“懒”啊?更新推送总是慢吞吞的,让人等得花儿都谢了。别急,今天就来给...
ape格式转换安卓系统,享受音... 你有没有想过,你的安卓手机里的ape格式音乐文件,竟然可以通过一个小小的转换,焕发出全新的生命力?没...
获取安卓系统加载器,核心功能与... 你有没有想过,你的安卓手机里那些神奇的软件和游戏是怎么被安装到你的设备上的呢?没错,就是通过一个叫做...
安卓系统文件夹在哪,安卓系统文... 你有没有遇到过这样的情况:手机里乱糟糟的,想找个文件却找不到?别急,今天就来给你揭秘安卓系统文件夹的...
安卓手感最好的裸机系统,安卓手... 安卓手感最好的裸机系统:探索极致体验的秘密武器在数字世界中,我们常常被各种功能和复杂操作所包围,尤其...
nas如何刷回安卓系统,轻松刷... 你有没有想过,你的NAS(网络附加存储)突然间变成了一个安卓的小天地?别急,这可不是什么天方夜谭,而...
荣耀沿用的安卓系统吗,打造个性... 你有没有注意到,最近荣耀的新机发布,大家都在热议一个问题:荣耀沿用的安卓系统吗?这可是个让人好奇不已...
快麦erp系统安卓下载,一键下... 你有没有听说最近一款叫做快麦ERP系统的软件在安卓平台上大受欢迎呢?没错,就是那个能让你企业管理如虎...
华为安卓系统下载app,一步到... 你有没有发现,最近华为手机的用户们都在忙活一件大事儿?没错,那就是下载安卓系统上的各种app啦!这可...
原生安卓系统游戏模式,畅享沉浸... 亲爱的手机游戏爱好者们,你是否曾为手机游戏运行不畅而烦恼?又或者,你是否渴望在游戏中获得更极致的体验...
安卓9改系统语言设置,轻松切换... 你有没有发现,手机里的语言设置有时候真的让人头疼?比如说,你突然想用一下安卓9的系统语言设置,结果发...
怎么升级安卓最新系统,畅享安卓... 亲爱的手机控们,你是不是也和我一样,对安卓系统的更新充满了期待?每次系统升级,都仿佛给我们的手机带来...
安卓系统电视跳舞毯,家庭娱乐新... 你有没有想过,家里的电视除了用来追剧、看电影,还能变成一个充满活力的娱乐中心?没错,我要给你介绍的就...
安卓系统维护周期,全方位守护您... 亲爱的手机控们,你是不是也和我一样,对安卓系统的维护周期充满了好奇呢?毕竟,我们的手机可是我们日常生...
安卓系统电脑怎么往下滑,一扫即... 你有没有发现,用安卓系统电脑的时候,有时候屏幕上会出现一些小图标或者应用,你想要快速浏览或者切换,却...
手机中判断安卓系统苹果系统js... 你有没有想过,你的手机里到底装的是安卓系统还是苹果系统呢?这可不是一个小问题哦,因为不同的系统,就像...
window系统和安卓系统还原... 你有没有遇到过手机或电脑突然卡顿,或者不小心删掉了重要的文件?别急,今天就来给你详细说说如何让win...
安卓系统打电话变声器,轻松实现... 安卓系统打电话变声器:探索数字时代的通信革新在数字化浪潮中,智能手机已经成为我们生活中不可或缺的一部...