【Q739】(md) 每日温度
请根据每日 气温列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]
class Solution {
/*
* 【单调栈】
*
* 解决这类问题,也就是寻找某处的左/右第一个比它大/小的值 的问题,
* 首先暴力法优化一下,就是【中心扩散法】
* 再优化,就可以在线性条件下实现,也就是【单调栈】
*
* 本题中,维护了一个递减的单调栈,每当遇到严格大于栈顶的数时,就用while开始尝试pop
* 另外别忘了,栈中的值是下标,因为单调栈本身已经表示了大小关系,我们需要得到的是【距离】
*/
public int[] dailyTemperatures(int[] T) {
Deque<Integer> stack = new ArrayDeque<>();
int len = T.length;
int[] res = new int[len];
for(int i = 0; i < len; i++) {
while(!stack.isEmpty() && T[i] > T[stack.peek()]) {
int left = stack.pop();
res[left] = i - left;
}
stack.push(i); // 这里因为写成了add,debug了好久...
}
return res;
}
/*
* 这道题与 [leetcode Q287柱状图中的最大矩形](Sama的个人记录15) 有异曲同工之妙
* 甚至更简单:"柱状图最大矩形"的核心思路是寻找某一处的左右边界(第一个比它小的柱子),而本题中,只需要找到【右边的第一个比它大的值】
*/
}
【Q42】(hd) 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例: 输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6
class Solution {
/*
* 【单调栈】
*
* 维护一个【严格单调递增单调栈】 可以获取某个元素前面和后面的第一个[非严格小于]它的柱子
* 维护一个【严格单调递减单调栈】 可以获取某个元素前面和后面的第一个[非严格大于]它的柱子
*
* 接雨水时,能形成容量的,就是某个元素左右两侧[非严格大于]它的柱子
* 因此我们维护一个【严格、单调递减的单调栈】
*
* 每次遇到比栈顶元素大的元素时,它就是右边界柱子,pop出栈顶后,此时的栈顶(peek获取)就是左边界柱子
* 底是左右两侧中间夹着的长度
* 高是左右边界中较低的那个柱子(简单的短板效应)
*
* 容量cap = 底 * 高
* 等等,还没结束呢!这是只考虑左右边界的理想容量,实际的容量会更小———这个容器被一些块给占了一些
* 解决的方案很简单:遍历左右柱子中间的部分,cpa减去对应的值
*
* 等等!还有问题!我们每次在尝试出栈时,用的是一个while循环,这导致容量会重复:
* 比如[2, 1, 0, 2],下标1到3之间,容量为1;while继续出栈,下标0和3之间,容量为3 ——— 这样一共就是4了,实际容量为3
* 解决方案是【填海】,每次形成一个容器后,计算过容量,就把这个容器填上,免得再被算一次
* 就[2, 1, 0, 2]的例子而言,计算出容量为一后,填上,变为[2, 1, 1, 2],那么再出栈计算出的下个容器容量就为2, 一共1+2=3
*/
public int trap(int[] height) {
Deque<Integer> stack = new ArrayDeque<>();
int capcaity = 0;
int len = height.length;
for(int i = 0; i < len; i++) {
while(!stack.isEmpty() && height[i] >= height[stack.peek()]) {
stack.pop();
if(stack.isEmpty()) { // 左边界有可能不存在,直接去peek有可能出错,先判断一下:没有左边界柱子,容量就是0,自然不用接着往下算了
continue;
}
int h = Math.min(height[stack.peek()], height[i]); // 高是左右边界柱子的最小值(简单的短板效应)
int w = i - stack.peek() - 1; // 底是左右边界柱子之间的宽度
int cap = w * h;
for(int j = stack.peek() + 1; j < i; j++) { // 修正容量,把容器中的块减去;同时把这个容器填满(变成一个长方体)
cap -= height[j];
height[j] = h;
}
capcaity += cap;
}
stack.push(i); // 别忘了push入栈啊 >_<
}
return capcaity;
}
/*
* 有一种思路可以避免【填海】行为:
* 每个柱子接到的雨水等于这个柱子左右两边最大值中较小者减去柱子本身的高度
* 【每个柱子接到的雨水】这句话抽象脱离的现实,但不难理解
*/
}
【Q70】(ez) 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
示例 2:
输入: 5
输出: 8 :
// 【动态规划】
// 转移方程很简单:dp[i] = dp[i - 1] + dp[i - 2]
public int climbStairs(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i < n + 1; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// 等等,这是...斐波那契数列?! 那么我们可以递归实现
//(其实会超时,很不推荐)
public int climbStairs(int n) {
return fib(n);
}
private int fib(int n) {
if(n == 0 || n == 1) {
return 1;
}else {
return fib(n - 1) + fib(n - 2);
}
}
// 重新审视一下我们的DP表:其实我们并不需要这么一个额外空间,我们只需要维护两个数的值就可以!
//【优化1】
public int climbStairs(int n) {
int res = 1;
int pre = 1;
int next = 1;
for(int i = 2; i < n + 1; i++) {
res = pre + next;
pre = next;
next = res;
}
return res;
}
// 干脆再狠一些
//【优化2】
public int climbStairs(int n) {
int pre = 1;
int next = 1;
for(int i = 2; i < n + 1; i++) {
next = pre + next;
pre = next - pre; // 直接不好理解,写一下fib的前几个值,就显而易见了
}
return next;
}
// 还没有结束,通过数学推导,写出递推公式,实现O(1)复杂度!
public int climbStairs(int n) {
double sqrt5 = Math.sqrt(5);
double fibn = Math.pow((1 + sqrt5) / 2, n + 1) - Math.pow((1 - sqrt5) / 2, n + 1);
return (int)(fibn / sqrt5);
}
// 总结:人外有人,天外有天
Qs from https://leetcode-cn.com
♦ loli suki
♥ end
本文深入解析单调栈在求解每日温度变化及接雨水问题中的应用,同时探讨动态规划解决爬楼梯问题的多种高效算法。从基本原理到优化技巧,全面覆盖算法设计与实现。


1665

被折叠的 条评论
为什么被折叠?



