lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1. 题目

 

 

2. 解法

有两种常见的方法:

  • 一种是使用数组和滑动窗口,
  • 另一种是使用单调栈。

 

数组和滑动窗口

思路

使用数组和滑动窗口的方法的思路是:

  • 对于每个元素,如果我们知道它左右两边第一个比它小的元素的下标,那么我们就可以计算出以它为高度的最大矩形的宽度,即右边界减去左边界再减一。
  • 为了找到每个元素的左右边界,我们可以使用两个数组left和right来存储它们。对于left数组,我们从左往右遍历原数组,用一个临时变量tmp来记录当前元素的左边界,初始值为i-1。然后我们比较tmp和i位置的元素,如果tmp位置的元素大于等于i位置的元素,说明tmp不是i的左边界,我们需要更新tmp为left[tmp],即tmp位置元素的左边界。这样我们就可以跳过一些不必要的比较,直到找到第一个比i位置元素小的元素或者越界。最后我们把tmp赋值给left[i]。对于right数组,我们从右往左遍历原数组,用类似的方法来更新right[i]。
  • 在得到了left和right数组后,我们再遍历一遍原数组,对于每个元素,计算以它为高度的最大矩形面积,并更新最大值。

代码逻辑

 

使用数组和滑动窗口的方法的代码逻辑是:

  • 定义一个函数largestRectangleArea,接受一个整型数组heights作为参数,返回一个整型值作为结果。
  • 判断heights的长度是否为0或1,如果是,则直接返回heights[0]或0。
  • 定义两个整型数组left和right,分别用来存储每个元素的左右边界,初始化left[0]为-1,right[len-1]为len。
  • 定义一个整型变量len,用来存储heights的长度。
  • 用一个for循环从1到len-1遍历heights数组,定义一个整型变量tmp,初始化为i-1。用一个while循环判断tmp是否大于等于0且heights[tmp]是否大于等于heights[i],如果是,则更新tmp为left[tmp]。最后把tmp赋值给left[i]。
  • 用一个for循环从len-2到0遍历heights数组,定义一个整型变量tmp,初始化为i+1。用一个while循环判断tmp是否小于len且heights[tmp]是否大于等于heights[i],如果是,则更新tmp为right[tmp]。最后把tmp赋值给right[i]。
  • 定义一个整型变量maxArea,用来存储最大矩形面积,初始化为0。
  • 用一个for循环从0到len-1遍历heights数组,定义一个整型变量width,用来存储以当前元素为高度的最大矩形的宽度,计算方法为right[i]-left[i]-1。定义一个整型变量area,用来存储以当前元素为高度的最大矩形面积,计算方法为width*heights[i]。用maxArea和area比较并更新maxArea。
  • 返回maxArea作为结果。

具体实现

public int largestRectangleArea(int[] heights) {
    int len = heights.length;
    if (len == 0) {
        return 0;
    }
    if (len == 1) {
        return heights[0];
    }

    // 使用数组来模拟栈,避免频繁的出栈入栈操作
    int[] left = new int[len];
    int[] right = new int[len];

    // 初始化
    left[0] = -1;
    right[len - 1] = len;

    // 遍历数组,计算每个元素左边第一个比它小的元素的下标
    for (int i = 1; i < len; i++) {
        int tmp = i - 1;
        while (tmp >= 0 && heights[tmp] >= heights[i]) {
            tmp = left[tmp];
        }
        left[i] = tmp;
    }

    // 遍历数组,计算每个元素右边第一个比它小的元素的下标
    for (int i = len - 2; i >= 0; i--) {
        int tmp = i + 1;
        while (tmp < len && heights[tmp] >= heights[i]) {
            tmp = right[tmp];
        }
        right[i] = tmp;
    }

    // 遍历数组,计算每个元素能构成的最大矩形面积
    int maxArea = 0;
    for (int i = 0; i < len; i++) {
        int width = right[i] - left[i] - 1;
        int area = width * heights[i];
        maxArea = Math.max(maxArea, area);
    }

    return maxArea;
}

  

 

使用单调栈

思路

使用单调栈的方法的思路是:

  • 为了找到每个元素的左右边界,我们可以使用一个单调递增的栈来存储元素的下标。这样,栈顶元素就是当前元素的左边界,而当遇到一个比栈顶元素小的元素时,就说明找到了栈顶元素的右边界。
  • 我们遍历数组,对于每个元素,我们先判断栈是否为空或者当前元素是否大于等于栈顶元素,如果是,则将当前元素的下标入栈。如果不是,则说明当前元素是栈顶元素的右边界,我们就弹出栈顶元素,并计算以它为高度的最大矩形面积。如果此时栈为空,说明栈顶元素没有左边界,否则栈顶元素就是它的左边界。我们重复这个过程,直到栈为空或者当前元素大于等于栈顶元素。
  • 在遍历完数组后,如果栈不为空,说明还有一些元素没有找到右边界,我们可以假设它们的右边界是数组的长度。我们再次弹出栈顶元素,并计算以它为高度的最大矩形面积。如果此时栈为空,说明栈顶元素没有左边界,否则栈顶元素就是它的左边界。我们重复这个过程,直到栈为空。

代码逻辑


使用单调栈的方法的代码逻辑是:

    • 定义一个函数largestRectangleArea,接受一个整型数组heights作为参数,返回一个整型值作为结果。
    • 判断heights的长度是否为0或1,如果是,则直接返回heights[0]或0。
    • 定义一个整型变量len,用来存储heights的长度。
    • 定义一个整型变量maxArea,用来存储最大矩形面积,初始化为0。
    • 定义一个整型栈stack,用来存储元素的下标,保持栈内元素对应的高度单调递增。
    • 用一个for循环从0到len-1遍历heights数组,定义一个整型变量height,用来存储当前元素的高度。
    • 用一个while循环判断栈是否为空或者height是否小于栈顶元素对应的高度,如果是,则定义一个整型变量curHeight,用来存储弹出的栈顶元素对应的高度。定义一个整型变量leftBoundary,用来存储弹出的栈顶元素的左边界,如果此时栈为空,则赋值为-1,否则赋值为栈顶元素。定义一个整型变量rightBoundary,用来存储弹出的栈顶元素的右边界,赋值为当前遍历到的下标i。定义一个整型变量width,用来存储以弹出的栈顶元素为高度的最大矩形的宽度,计算方法为rightBoundary-leftBoundary-1。定义一个整型变量area,用来存储以弹出的栈顶元素为高度的最大矩形面积,计算方法为width*curHeight。用maxArea和area比较并更新maxArea。
    • 在while循环结束后,将当前遍历到的下标i入栈。
    • 在for循环结束后,如果栈不为空,则说明还有一些元素没有找到右边界。我们再次用一个while循环

具体实现

public int largestRectangleArea(int[] heights) {
    int len = heights.length;
    if (len == 0) {
        return 0;
    }
    if (len == 1) {
        return heights[0];
    }

    // 使用栈来存储元素下标,保持栈内元素对应的高度单调递增
    Stack<Integer> stack = new Stack<>();
    
    // 遍历数组,对于每个元素,找到它左边和右边第一个比它小的元素的下标
    int maxArea = 0;
    for (int i = 0; i < len; i++) {
        int height = heights[i];
        
        // 如果栈不为空且当前高度小于栈顶高度,说明找到了右边界
        while (!stack.isEmpty() && height < heights[stack.peek()]) {
            // 弹出栈顶元素,计算以它为高度的最大矩形面积
            int curHeight = heights[stack.pop()];
            // 如果栈为空,说明左边界不存在,否则左边界就是栈顶元素
            int leftBoundary = stack.isEmpty() ? -1 : stack.peek();
            // 右边界就是当前元素
            int rightBoundary = i;
            // 计算宽度和面积
            int width = rightBoundary - leftBoundary - 1;
            int area = width * curHeight;
            maxArea = Math.max(maxArea, area);
        }
        
        // 将当前元素下标入栈
        stack.push(i);
    }
    
// 如果遍历完数组后栈不为空,说明还有一些元素没有找到右边界
while (!stack.isEmpty()) {
    // 弹出栈顶元素,计算以它为高度的最大矩形面积
    int curHeight = heights[stack.pop()];
    // 如果栈为空,说明左边界不存在,否则左边界就是栈顶元素
    int leftBoundary = stack.isEmpty() ? -1 : stack.peek();
    // 右边界就是数组的长度
    int rightBoundary = len;
    // 计算宽度和面积
    int width = rightBoundary - leftBoundary - 1;
    int area = width * curHeight;
    maxArea = Math.max(maxArea, area);
}

return maxArea;
}

  

 

 

3. 总结

posted on 2023-04-25 13:53  白露~  阅读(16)  评论(0编辑  收藏  举报