84. 柱状图中最大的矩形

栈方法

class Solution {
    public int largestRectangleArea(int[] heights) {
        //定义新数组,使得访问栈时不用考虑栈为空的情况
        int[] new_heights = new int[heights.length + 1];
        //用栈存位置
        Deque<Integer> stack = new LinkedList<Integer>();
        stack.addLast(0);
        int max_area = 0;

        //复制数组,且数组尾部置为0
        for(int i = 0; i < heights.length; i++){
            new_heights[i] = heights[i];
        }
        new_heights[heights.length] = 0;

        /*
        栈内位于上面的边总是长于位于下面的边,且当遇到比栈顶短的边,
        就求出高度为栈顶的边的长的矩形的面积,并与max_area进行比较
        得到较大的值。
         */
        for(int i = 0; i < new_heights.length; i++){
            while (!stack.isEmpty() && new_heights[stack.peekLast()] > new_heights[i]){
                //弹出凸出的边
                int peek = stack.removeLast();

                //长度相同的边就弹出来,且左边往左移,宽度增加
                while (!stack.isEmpty() && new_heights[stack.peekLast()] == new_heights[peek]){
                    peek = stack.removeLast();
                }

                /*
                如果找不到左边,即栈为空,则此时new_heights[peek]为数组里
                最小的元素,以它为高度的矩形的宽度为heights[]的长度,又此时
                i == heights.lenght,所以将left设为0

                如果找到了左边,即栈不为空,则将左边的位置设为栈顶的元素,
                以new_heights[peek]为高度的矩形的宽度为中间的宽度, 例如
                [1, 3, 4, 2],当矩形高度为2时,宽度为1和2之间的宽度,即为2
                所以left = stack.peekLast() + 1
                 */
                int left = stack.isEmpty() ? 0 : stack.peekLast() + 1;
                max_area = Math.max(new_heights[peek] * (i - left), max_area);
            }
            stack.addLast(i);
        }

        return max_area;
    }
}

分治算法

class Solution {
    public int calculateArea(int[] heights, int start, int end){
        if(start > end){
            return 0;
        }

        int min_i = start;

        for(int i = start; i <= end; i++){
            if(heights[min_i] > heights[i]){
                min_i = i;
            }
        }

        return Math.max(heights[min_i] * (end - start + 1),
                Math.max(calculateArea(heights, start, min_i -  1),
                calculateArea(heights, min_i + 1, end)));
    }

    public int largestRectangleArea(int[] heights) {
        return calculateArea(heights, 0, heights.length - 1);
    }
}

线段树 + 分治算法

//线段树
class SegTreeNode{
    //线段的起点和终点
    public int start_;
    public int end_;
    //左结点,右结点
    public SegTreeNode l_;
    public SegTreeNode r_;
    //该线段结点的最小值的位置
    public int min_;

    SegTreeNode(){}
    SegTreeNode(int start, int end){
        this.start_ = start;
        this.end_ = end;
        this.l_ = this.r_ = null;
    }
}


class Solution {
    //递归建立树
    public SegTreeNode buildTree(int[] heights, int start, int end){
        if(start > end) return null;

        SegTreeNode root = new SegTreeNode(start, end);

        if(start == end){
            root.min_ = start;
            return root;
        }

        int mid = (start + end) / 2;

        //递归建树
        root.l_ = buildTree(heights, start, mid);
        root.r_ = buildTree(heights, mid + 1, end);
        root.min_ = heights[root.l_.min_] < heights[root.r_.min_] ? root.l_.min_ : root.r_.min_;

        return root;
    }


    //查询最小值
    public int query(SegTreeNode root, int[] heights, int start, int end){
        //如果结点为空,且查询区间与该结点区间不相交,则返回-1
        if(root == null || start > root.end_ || end < root.start_) return -1;
        //如果结点不为空,且查询区间与该结点区间重合或包含了该结点区间,则返回该结点区间的最小值
        if(start <= root.start_ && end >= root.end_){
            return root.min_;
        }

        /*
        如果查询区间与该结点区间相交且不重合,则分别查询结点的左子树和右子树
        返回最小值的位置
         */
        int left_min = query(root.l_, heights, start, end);
        int right_min = query(root.r_, heights, start, end);
        if(left_min == -1) return right_min;
        if(right_min == -1) return left_min;
        //返回heights较小的位置
        return heights[left_min] < heights[right_min] ? left_min : right_min;
    }

    //线段树 + 分治算法计算区间内矩形的最大面积
    public int calculateArea(SegTreeNode root, int[] heights, int start, int end){
        if(start > end){
            return -1;
        }

        if(start == end){
            return heights[start];
        }

        //寻找该区间的最小值的位置
        int min_i = query(root, heights,start, end);
        //寻找位于最小值左边的区间的矩形的最大面积
        int left_max = calculateArea(root, heights, start, min_i - 1);
        //寻找位于最小值右边的区间的矩形的最大面积
        int right_max = calculateArea(root, heights, min_i + 1,end);

        //返回最大值
        return Math.max(heights[min_i] * (end - start + 1),
                Math.max(left_max, right_max));
    }

    public int largestRectangleArea(int[] heights) {
        if(heights.length == 0) return 0;
        
        SegTreeNode root = buildTree(heights, 0, heights.length - 1);;

        return calculateArea(root, heights, 0, heights.length - 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值