0
点赞
收藏
分享

微信扫一扫

算法-leetcode-栈和队列- 84. 柱状图中最大的矩形

早安地球 2022-04-16 阅读 80

文章目录

23, 84. 柱状图中最大的矩形

思路1: 暴力解法

思路2:双指针法

思路3:思路2优化:跳过不需要的指针递减或者递增

思路4: 单调栈

思路5:思路4优化:一次遍历,同时找到左右边界

package com.shangguigu.dachang.algrithm.A07_queue_and_stack;

import java.util.Stack;

/**
 * @author : 不二
 * @date : 2022/4/16-上午11:34
 * @desc : 84. 柱状图中最大的矩形
 * https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
 **/
public class A73_largestRectangleArea {

    public static void main(String[] args) {
        int[] heights = {2, 1, 5, 6, 2, 3};
//        int[] heights = {2, 3};
//        int[] heights = {2, 4};
//        int[] heights = {1, 1};
//        int i = largestRectangleArea(heights);
//        int i = largestRectangleArea_v1(heights);
//        int i = largestRectangleArea_v2(heights);
//        int i = largestRectangleArea_v3(heights);
        int i = largestRectangleArea_v4(heights);
        System.out.println("结果是:" + i);
    }

    /**
     * 思路5:思路4优化:一次遍历,同时找到左右边界
     *       遍历求出左边界的同时:应该知道:
     *       第一次弹出栈的那个数据就是被弹索引数据的右边界
     *
     *       这个好难理解。。。。。。
     *
     */
    public static int largestRectangleArea_v4(int[] heights){

        int maxArea = 0;
        int[] leftBoundary = new int[heights.length];
        int[] rightBoundary = new int[heights.length];

        // 先初始化右边界。只要没有被弹出来,说明右边的数据都比当前的这个大,右边界就是结尾。
        for (int i = 0; i < heights.length; i++) {
            rightBoundary[i] = heights.length-1;
        }
        Stack<Integer> stack = new Stack<>();


        // 根据栈结构进行计算左边界
        for (int i = 0; i < heights.length; i++) {
            // 如果当前栈顶的元素是大于等于 当前元素的 --> 说明是值下降来
            while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {

                // 这里是优化点:被弹的数据的有边界必然是i
                // 因为i数据小于peek数据,peek是i的左边界
                // 相对的:peek 数据 大于i数据,那么从右往左看, peek的右边界就是i-1
                rightBoundary[stack.peek()] = i-1;

                // 把当前元素弹出
                stack.pop();
            }

            // 当弹出结束后,此时栈顶的元素就是:左边界的索引了
            // 如果栈中为空了,那么左边界就是从0开始
            leftBoundary[i] = stack.isEmpty() ? 0 : stack.peek() + 1;
            // 把索引i放入栈中
            stack.push(i);
        }


        // 这里直接在第一次遍历的时候直接计算出右边界,这里就不需要了
        // 到这里的话,stack的作用已经完成,左边界已经计算结束
        /*stack.clear();
        // 根据栈结构计算右边界
        for (int i = heights.length - 1; i >= 0; i--) {
            // 如果当前栈顶的元素是大于等于 当前元素的 --> 说明是值下降来
            while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
                // 把当前元素弹出
                stack.pop();
            }
            // 当弹出结束后,此时栈顶的元素就是:左边界的索引了
            // 如果栈中为空了,说明刚才遍历过的所有值都比当前值大,那么左边界就是从最右开始
            rightBoundary[i] = stack.isEmpty() ? heights.length-1 : stack.peek() - 1;
            // 把索引i放入栈中
            stack.push(i);
        }*/

        // System.out.println("-------");
        // 根据左右边界和高度计算面积
        for (int i = 0; i < heights.length; i++) {
            int height = heights[i];
            int left = leftBoundary[i];
            int right = rightBoundary[i];
            int area = height * (right - left + 1);
            maxArea = Math.max(area, maxArea);
        }

        return maxArea;
    }


    /**
     * 思路4: 单调栈
     *
     * 把寻找左右边界的过程使用栈来实现
     *
     */
    public static int largestRectangleArea_v3(int[] heights){

        int maxArea = 0;
        int[] leftBoundary = new int[heights.length];
        int[] rightBoundary = new int[heights.length];

        Stack<Integer> stack = new Stack<>();


        // 根据栈结构进行计算左边界
        for (int i = 0; i < heights.length; i++) {
            // 如果当前栈顶的元素是大于等于 当前元素的 --> 说明是值下降来
            while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
                // 把当前元素弹出
                stack.pop();
            }
            // 当弹出结束后,此时栈顶的元素就是:左边界的索引了
            // 如果栈中为空了,那么左边界就是从0开始
            leftBoundary[i] = stack.isEmpty() ? 0 : stack.peek() + 1;
            // 把索引i放入栈中
            stack.push(i);
        }


        // 到这里的话,stack的作用已经完成,左边界已经计算结束
        stack.clear();
        // 根据栈结构计算右边界
        for (int i = heights.length - 1; i >= 0; i--) {
            // 如果当前栈顶的元素是大于等于 当前元素的 --> 说明是值下降来
            while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
                // 把当前元素弹出
                stack.pop();
            }
            // 当弹出结束后,此时栈顶的元素就是:左边界的索引了
            // 如果栈中为空了,说明刚才遍历过的所有值都比当前值大,那么左边界就是从最右开始
            rightBoundary[i] = stack.isEmpty() ? heights.length-1 : stack.peek() - 1;
            // 把索引i放入栈中
            stack.push(i);
        }

        // System.out.println("-------");
        // 根据左右边界和高度计算面积
        for (int i = 0; i < heights.length; i++) {
            int height = heights[i];
            int left = leftBoundary[i];
            int right = rightBoundary[i];
            int area = height * (right - left + 1);
            maxArea = Math.max(area, maxArea);
        }

        return maxArea;
    }



    /**
     * 思路3:思路2优化
     * 思路2的时间复杂度也是O(n*n)
     * 如果左边数字比当前位置大,那么直接跳到左边数字的左边界
     * 如果右边数字也比当前位置大,那么直接跳到右边数字大右边界
     */
    public static int largestRectangleArea_v2(int[] heights){
        int maxArea = 0;
        int[] leftBoundary = new int[heights.length];
        int[] rightBoundary = new int[heights.length];

        // 先找左边界,记录下来----得从左往右找
        for (int i = 0; i < heights.length; i++) {
            int left = i;
            while (left > 0 && heights[i] <= heights[left-1]) {
                // left--;
                // 这里重点!!!
                left = leftBoundary[left - 1];
            }
            // 如果走出来,说明已经找到左边界
            leftBoundary[i] = left;
        }

        // 再找右边界,记录下来----得从右往左找
        for (int i = heights.length - 1; i >= 0; i--) {
            // int height = heights[i];
            int right = i;
            while (right < heights.length - 1 && heights[i] <= heights[right + 1]) {
                // left--;
                // 这里重点!!!
                right = rightBoundary[right + 1];
            }
            // 如果走出来,说明已经找到左边界
            rightBoundary[i] = right;
        }

        // 根据左右边界和高度计算面积
        for (int i = 0; i < heights.length; i++) {
            int height = heights[i];
            int left = leftBoundary[i];
            int right = rightBoundary[i];
            int area = height * (right - left + 1);
            maxArea = Math.max(area, maxArea);
        }

        return maxArea;
    }


    /**
     * 思路2:双指针法
     * 指定高度,横向扩展看下能扩展到什么地方
     *
     */
    public static int largestRectangleArea_v1(int[] heights){

        int leftPointer,rightPointer;
        int maxArea = 0;
        for (int i = 0; i < heights.length; i++) {
            // 把i位置的数据当成高度(也是是最低),横向扩展。
            int theHeight = heights[i];
            leftPointer = rightPointer = i;
            while (leftPointer > 0 && heights[i] <= heights[leftPointer - 1]) {
                leftPointer--;
            }
            while (rightPointer < heights.length-1 && heights[i] <= heights[rightPointer + 1]) {
                rightPointer++;
            }

            int wide = (rightPointer-leftPointer)+1;
            int area = theHeight * wide;
            maxArea = Math.max(area, maxArea);
        }
        return maxArea;
    }


    /**
     * 思路1:暴力解法
     * 这里是根据遍历宽度,找到对应的高度
     *
     */
    public static int largestRectangleArea(int[] heights) {
        int area = 0;
        for (int i = 0; i < heights.length; i++) {
            int minHeight = Integer.MAX_VALUE;
            // 遍历宽度,每个宽度找到其对应的高度,算出面积
            for (int j = i; j < heights.length; j++) {
                // 找到这组数据中最小的高度
                minHeight = Math.min(heights[j], minHeight);
                int wide = j - i + 1;
                int areaS = wide * minHeight;
                area = Math.max(area, areaS);
            }
        }
        return area;
    }
}
举报

相关推荐

0 条评论