剑指 Offer II 039. 直方图最大矩形面积(84. 柱状图中最大的矩形)

题目:

思路:

【1】以中心扩散为思路的暴力破解方法

 

 

【2】单调栈的方式(基础模型:在一维数组中对每一个数找到第一个比自己小的元素。这类“在一维数组中找第一个满足某种条件的数”的场景就是典型的单调栈应用场景。)【其实可以参考  剑指 Offer 30. 包含min函数的栈  】

代码展示:

单调栈 + 常数优化:

//时间18 ms击败81.16%
//内存50 MB击败91.3%
class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] left = new int[n];
        int[] right = new int[n];
        Arrays.fill(right, n);
        
        Deque<Integer> mono_stack = new ArrayDeque<Integer>();
        for (int i = 0; i < n; ++i) {
            while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
                right[mono_stack.peek()] = i;
                mono_stack.pop();
            }
            left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
            mono_stack.push(i);
        }
        
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
        }
        return ans;
    }
}

 

 

 

单调栈的方式:

//时间24 ms击败52.4%
//内存55.7 MB击败29.11%
//时间复杂度:O(N)。
//空间复杂度:O(N)。
class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] left = new int[n];
        int[] right = new int[n];
        //以{2,1,5,6,2,3}为例
        Deque<Integer> mono_stack = new ArrayDeque<Integer>();
        for (int i = 0; i < n; ++i) {
            //如果不为空则存在下标,下标位置的值如果比当前值要大则弹出,因为只会保留距离当前值最近的左边的最小值
            while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
                mono_stack.pop();
            }
            //如果不为空则发现附近的最小值,没有则会延伸至边界
            left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
            //这里是记录下标
            mono_stack.push(i);
        }
        System.out.println(Arrays.toString(left));

        mono_stack.clear();
        for (int i = n - 1; i >= 0; --i) {
            while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
                mono_stack.pop();
            }
            right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek());
            mono_stack.push(i);
        }

        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
        }
        return ans;
    }
}

 

 

 

以中心扩散为思路进行的优化:

//时间6 ms击败96.90%
//内存54.8 MB击败40.61%
class Solution {
    public int largestRectangleArea(int[] heights) {
        int max = 0;
        int left = 0;
        int right = 0;
        for(int i =0;i<heights.length;i++){
            left = i-1;
            right = i+1;
            //这里可以减少当数据量过大时候不必要的扩散
            //首先如果你扩散到了极致(即整个数组的长度),配合你的高度都比不过我现在的最大值,那么你就没有扩散的必要了
            if(heights.length * heights[i]>max){
                while(left>=0 && heights[left]>=heights[i]){
                    left--;
                }
                while(right<=heights.length-1 && heights[right]>=heights[i]){
                    right++;
                }
                max = Math.max(max,(right - left - 1)*heights[i]);
            }
        }
        return max;
    }
}

 

以中心扩散为思路的暴力破解方法:

//在运行的时候是会出现超时的
//时间复杂度:O(N^2),这里 N 是输入数组的长度。
//空间复杂度:O(1)。
//因为本身就是基于每一个点都会向两边扩散,直至找到最大的矩阵范围(而且高度是基于当前位置的高度)
class Solution {
    public int largestRectangleArea(int[] heights) {
        int len = heights.length;

        int res = 0;
        for (int i = 0; i < len; i++) {

            // 找左边最后 1 个大于等于 heights[i] 的下标
            int left = i;
            int curHeight = heights[i];
            while (left > 0 && heights[left - 1] >= curHeight) {
                left--;
            }

            // 找右边最后 1 个大于等于 heights[i] 的索引
            int right = i;
            while (right < len - 1 && heights[right + 1] >= curHeight) {
                right++;
            }

            int width = right - left + 1;
            res = Math.max(res, width * curHeight);
        }
        return res;
    }
}

 

posted @ 2023-04-27 12:07  忧愁的chafry  阅读(16)  评论(0编辑  收藏  举报