Leetcode刷题84. 柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

Leetcode刷题84. 柱状图中最大的矩形_第1张图片

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。

Leetcode刷题84. 柱状图中最大的矩形_第2张图片

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

示例:

输入: [2,1,5,6,2,3]
输出: 10

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
        public int largestRectangleArea(int[] heights) {
//            return largestRetangleAreaI(heights);
//            return largestRectangleAreaII(heights);
//            return largestRectangleAreaIII(heights);
            return largestRectangleAreaIII(heights);
        }

        //1.首先找到i位置最大面积
        //2.以i为中心,向左找第一个小于heights[i]的位置leftLessMin;向右找第一个小于heights[i]的位置rightLessMin
        //3.最大面积为heights[i] * (rightLessMin - leftLessMin - 1)
        //如何找到rightLessMin和leftLessMin?

        //方法一:暴力法,时间复杂度O(n^2)
        //左右移动,遍历
        private int largestRetangleAreaI(int[] heights) {
            int result = 0;
            int len = heights.length;
            for (int i = 0; i < len; i++) {
                int left = i, right = i;
                while (left >= 0 && heights[left] >= heights[i]) left--;
                while (right < len && heights[right] >= heights[i]) right++;
                result = Math.max(result, (right - left - 1) * heights[i]);
            }
            return result;
        }

        //方法二:基于方法一优化
        //定义一个数组leftLessMin[]保存各自的左边第一个小的柱子
        //第一个柱子前没有柱子,赋值为-1 leftLessMin[0] = -1
        //当前柱子i比上一柱子小的时候
        //由于之前已经求出上一柱子的leftLessMin[i - 1],也就是第一个比上个柱子小的柱子
        //所以可以直接从leftLessMin[i - 1]开始比较,因为从leftLessMin[i - 1] + 1到i - 1都是比当前柱子i高,可以直接跳过
        private int largestRectangleAreaII(int[] heights) {
            if (heights.length == 0) return 0;
            int[] leftLessMin = new int[heights.length];
            leftLessMin[0] = -1;
            for (int i = 1; i < heights.length; i++) {
                int l = i - 1;
                //当前柱子更小
                while (l >= 0 && heights[l] >= heights[i]) {
                    l = leftLessMin[l];
                }
                leftLessMin[i] = l;
            }
            int[] rightLessMin = new int[heights.length];
            //最后一个柱子的右边实际没有柱子,赋值为数组长度
            rightLessMin[heights.length - 1] = heights.length;
            for (int i = heights.length - 2; i >= 0; i--) {
                int r = i + 1;
                while (r <= heights.length - 1 && heights[r] > heights[i]) {
                    r = rightLessMin[r];
                }
                rightLessMin[i] = r;
            }

            //求面积
            int result = 0;
            for (int i = 0; i < heights.length; i++) {
                result = Math.max(result, (rightLessMin[i] - leftLessMin[i] - 1) * heights[i]);
            }
            return result;
        }

        //方法三:基于方法二的思想使用栈
        //1.当前栈为空,或者当前柱子大于栈顶元素高度,将当前柱子的下标入栈
        //2.当前柱子高度小于栈顶元素,将栈顶出栈,当做当前要求面积的柱子
        //3.右边第一个小于当前柱子的下标为当前在遍历的柱子,左边第一个小于当前柱子的下标为当前新的栈顶
        //4.遍历结束后,如果栈没有空,依次出栈,出栈元素当做要求面积的柱子,然后依次计算面积
        private int largestRectangleAreaIII(int[] heights) {
            int result = 0, p = 0;
            Stack stack = new Stack<>();
            while (p < heights.length) {
                //栈为空,入栈
                if (stack.isEmpty()) {
                    stack.push(p++);
                } else {
                    //判断栈顶元素与入栈元素高度大小
                    int top = stack.peek();
                    //入栈元素大于栈顶元素,入栈
                    if (heights[p] >= heights[top]) {
                        stack.push(p++);
                    } else {
                        //否则,栈顶元素出栈
                        int height = heights[stack.pop()];
                        //左边第一个小于当前柱子的下标为新栈顶元素
                        int leftLessMin = stack.isEmpty() ? -1 : stack.peek();
                        //右边第一个小于当前柱子的下标为当前正遍历柱子
                        int rightLessMin = p;
                        //计算面积
                        result = Math.max(result, (rightLessMin - leftLessMin - 1) * height);
                    }
                }
            }
            //边界元素
            while (!stack.isEmpty()) {
                //依次出栈,出栈元素为当前要求面积的柱子
                int height = heights[stack.pop()];
                //左边第一个小于当前柱子的下标
                int leftLessMin = stack.isEmpty() ? -1 : stack.peek();
                //右边没有小于当前高度的柱子,赋值为数组的长度便于计算
                int rightLessMin = heights.length;
                result = Math.max(result, (rightLessMin - leftLessMin - 1) * height);
            }
            return result;
        }


        //方法四:分治法,时间复杂度 平均O(nlogn),最坏o(n^2)
        //问题可分为三种情况:
        //1.找出最短柱子,矩形的宽尽可能往两边延伸
        //2.在最短柱子的左边找出最大面积
        //3.在最短柱子的右边找出最大面积
        //举例[2,1,5,6,2,3]
        //最短柱子为1,宽度为6,面积1*6=6
        //对高度为1的左右两边采取同样的过程
        //1的左边,形成面积2*1=2;1的右边,最短柱子2,形成面积2*4=8,以此类推,2的左边形成5*2=10,6*1=6,2的右边3*1=3
        //最终得到面积最大值5*2=10
        private int largestRectangleAreaIIII(int[] heights) {
            return calculateMaxArea(heights, 0, heights.length - 1);
        }

        private int calculateMaxArea(int[] heights, int left, int right) {
            if (left > right) return 0;
            int minIndex = left;
            //求出最短柱子
            for (int i = left; i <= right; i++) {
                if (heights[i] < heights[minIndex]) {
                    minIndex = i;
                }
            }
            return Math.max(heights[minIndex] * (right - left + 1),
                    Math.max(calculateMaxArea(heights, left, minIndex - 1), calculateMaxArea(heights, minIndex + 1, right)));
        }
    }

 

你可能感兴趣的:(Leecode,分治算法,leetcode,动态规划)