【算法题】动态规划学习总结-3

99 阅读2分钟

今天完成 锻炼+ 学习打卡!把当舔狗的时间挤出来一点学习!

leetcode 84. 柱状图中最大的矩形

image.png

这道题用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;
}