今天完成 锻炼+ 学习打卡!把当舔狗的时间挤出来一点学习!
leetcode 84. 柱状图中最大的矩形
这道题用dp做是真难,也是借鉴了大佬的思路和题解,才想明白
- 这道题直接用dp来做,非常不好做,没有思路,连dp数组代表什么都不好定义,经常画了半天,然后推倒重来;
- 这种情况下,大佬告诉我们,先暴力做,找找思路,先暴力遍历找当前高度h[i]能形成的矩形,因为我们固定了左边界为i,右边界就是第一个小于h[i]的下标j-1(j-1 >=i),这样做时间复杂度on2,提交后不出意料超时了,
// 暴力解法,超时 on2
public int largestRectangleArea(int[] heights) {
int len = heights.length;
if (len == 0) return 0;
if (len == 1) return heights[0];
int max = 0;
for (int i =0;i<len;i++){
int minH = heights[i];
for (int j = i;j<len;j++){
minH = heights[j] < minH?heights[j]:minH;
int w = Math.abs(j - i + 1);
int area = w * minH;
if (area > max) max = area;
}
}
return max;
}
暴力解决后,我们有了一点点思路,我们第二层循环基本就是再找i右边第一个小于h[i]的下标,有大量重复运算,我们是不是可以存起来?用minRightIndex[i]来代表i右边第一个小于h[i]的下标,
- 而且我们不需要从i逐个遍历,我们从队尾开始,这样能复用i后面的minRightIndex值,
- 当h[t] > h[i]时,我们不要t++,而是考虑minRightIndex[t] 是t右边第一个小于t的值,也就是在t和minRightIndex[t]之间都是大于h[t],也就是大于h[i],于是t=minRightIndex[t]
- 左边的也是相同的道理 上述思路的代码如下
public int largestRectangleArea2(int[] heights) {
int len = heights.length;
if (len == 0) return 0;
if (len == 1) return heights[0];
int[] minLeftIndex = new int[len];
int[] minRightIndex = new int[len];
minLeftIndex[0] = -1;
for (int i =1;i<len;i++){
int t = i - 1;
while (t >= 0 && heights[t] >= heights[i]) {
t = minLeftIndex[t];
}
minLeftIndex[i] = t;
}
// 记录每个柱子 右边第一个小于该柱子的下标
minRightIndex[len - 1] = len; // 注意这里初始化,防止下面while死循环
for (int i = len - 2; i >= 0; i--) {
int t = i + 1;
// 这里不是用if,而是不断向右寻找的过程
while (t < len && heights[t] >= heights[i]) {
t = minRightIndex[t];
}
minRightIndex[i] = t;
}
int max = 0;
for (int i = 0; i < len; i++) {
int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
max = Math.max(sum, max);
}
return max;
}