力扣记录:单调栈——739 每日温度,496 下一个更大元素I,503 下一个更大元素II,42 接雨水,84 柱状图中最大的矩形

本次题目

  • 739 每日温度
  • 496 下一个更大元素I
  • 503 下一个更大元素II
  • 42 接雨水
  • 84 柱状图中最大的矩形

单调栈

  • 一维数组查找任一元素左边或右边比自己大或小的元素位置。

739 每日温度

  • 单调栈:
    1. 定义单调栈存储下标,栈头到栈底递增。定义结果数组并初始化为全0
    2. 遍历温度数组,如果当前元素大于栈顶元素,则取出栈顶元素,并赋值(当前下标减去栈顶元素)给结果数组对应下标;
    3. 直到栈顶元素小于当前元素或栈空,当前元素入栈,直到遍历结束。
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        //定义单调栈存储下标,栈头到栈底递增
        Deque<Integer> stack = new LinkedList<>();
        //定义结果数组并初始化为全0
        int[] result = new int[temperatures.length];
        //遍历温度数组
        for(int i = 0; i < temperatures.length; i++){
            //如果当前元素大于栈顶元素,则取出栈顶元素,并赋值(当前下标减去栈顶元素)给结果数组对应下标
            while(stack.peek() != null){
                if(temperatures[i] > temperatures[stack.peek()]){
                    int top = stack.pop();
                    result[top] = i - top;
                }else{//直到栈顶元素小于当前元素,当前元素入栈
                    stack.push(i);
                    break;
                }
            }
            //栈空,当前元素入栈
            if(stack.peek() == null){
                stack.push(i);
            }
        }
        //返回结果
        return result;
    }
}

496 下一个更大元素I

  • 同上739 每日温度,但是出栈时需要进行判断。
  • 单调栈:
    1. 定义结果数组,初始化为全-1,定义map记录数组1(子数组)中出现的元素及其下标
    2. 遍历数组2,如果当前元素大于栈顶元素,则取出栈顶元素,判断该元素是否在数组1中出现,若出现则赋当前元素对应值给结果数组对应位置;否则不处理。
  • 注意:初始化数组全部为-1,Arrays.fill(数组,-1)。
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        //定义结果数组,初始化为全-1
        int[] result = new int[nums1.length];
        for(int i = 0; i < result.length; i++){
            result[i] = -1;
        }
        //定义单调栈
        Deque<Integer> stack = new LinkedList<>();
        //定义map记录数组1(子数组)中出现的元素及其下标
        Map<Integer, Integer> map = new HashMap<>();
        for(int j = 0; j < nums1.length; j++){
            map.put(nums1[j], j);
        }
        //遍历数组2
        for(int k = 0; k < nums2.length; k++){
            while(stack.peek() != null){
                //如果当前元素大于栈顶元素,则取出栈顶元素
                if(nums2[k] > nums2[stack.peek()]){
                    int top = stack.pop();
                    //判断该元素是否在数组1中出现,若出现则赋当前元素对应值给结果数组对应位置
                    if(map.containsKey(nums2[top])){
                        result[map.get(nums2[top])] = nums2[k];
                    }
                }else{
                    stack.push(k);
                    break;
                }
            }
            if(stack.peek() == null) stack.push(k);
        }
        //返回结果
        return result;
    }
}

503 下一个更大元素II

  • 同上739 每日温度,但是本题数组为循环数组,可以将两个数组拼接在一起;也可以使用求余%模拟遍历两遍数组。
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        //定义单调栈存储下标,栈头到栈底递增
        Deque<Integer> stack = new LinkedList<>();
        //求数组长度
        int leng = nums.length;
        //定义结果数组并初始化为全-1
        int[] result = new int[leng];
        Arrays.fill(result, -1);
        //用求余%模拟遍历两遍数组
        for(int i = 0; i < leng * 2; i++){
            //如果当前元素大于栈顶元素,则取出栈顶元素,并赋值(当前下标减去栈顶元素)给结果数组对应下标
            while(stack.peek() != null){
                if(nums[i % leng] > nums[stack.peek()]){
                    int top = stack.pop();
                    result[top] = nums[i % leng];
                }else{//直到栈顶元素小于当前元素,当前元素入栈
                    stack.push((i % leng));
                    break;
                }
            }
            //栈空,当前元素入栈
            if(stack.peek() == null){
                stack.push((i % leng));
            }
        }
        //返回结果
        return result;
    }
}

42 接雨水

  • 双指针法:按列计算,当前列的雨水量为左侧最高和右侧最高中的较小值减去当前列高度。
    • 注意:第一列和最后一列不能接雨水。
  • DP:按列计算,不用重复计算左侧和右侧的最高高度,
    1. 遍历一遍保存到两个一维数组中(分别存当前列的左右侧最高)。
    2. min(左侧最高,右侧最高) – 当前高度;
    3. 遍历一遍进行初始化;
    4. i递增;
    5. 举例。
  • 单调栈:按行计算。
    1. 定义单调(递增)栈存储柱子对应下标;
    2. 如果当前元素高度小于栈头元素高度,则直接入栈;如果当前元素高度大于栈头元素高度,则栈头位置可以接雨水,雨水量为栈头下一个元高度素和当前元素高度的较小值减去栈头元素高度;
    3. 计算结束后弹出栈头元素直到当前元素(高度递增)入栈;如果当前元素高度和栈头元素高度相等,则替换栈头为当前元素。
  • 双指针:
class Solution {
    public int trap(int[] height) {
        //双指针
        //按列计算,当前列的雨水量为左侧最高和右侧最高中的较小值减去当前列高度
        //第一列和最后一列不能接雨水
        int sum = 0;
        for(int i = 1; i < height.length - 1; i++){
            //定义当前列左右侧最高
            int left = 0;
            int right = 0;
            //分别查找左右最高
            for(int j = i - 1; j >= 0; j--){
                left = Math.max(height[j], left);
            }
            for(int k = i + 1; k < height.length; k++){
                right = Math.max(height[k], right);
            }
            //求和
            int rain = Math.min(left, right) - height[i];
            if(rain > 0) sum += rain;
        }
        //返回结果
        return sum;
    }
}
  • DP:
class Solution {
    public int trap(int[] height) {
        //DP
        int[] dpLeft = new int[height.length];
        int[] dpRight = new int[height.length];
        //遍历一遍保存到两个一维数组中(分别存当前列的左右侧最高)
        //遍历一遍进行初始化
        for(int i = 1; i < height.length - 1; i++){
            int left = 0;
            int right = 0;
            for(int j = i - 1; j >= 0; j--){
                left = Math.max(left, height[j]);
            }
            for(int k = i + 1; k < height.length; k++){
                right = Math.max(right, height[k]);
            }
            dpLeft[i] = left;
            dpRight[i] = right;
        }
        //递推遍历
        int sum = 0;
        for(int j = 1; j < height.length - 1; j++){
            int rain = Math.min(dpLeft[j], dpRight[j]) - height[j];
            if(rain > 0) sum += rain;
        }
        //返回结果
        return sum;
    }
}
  • 单调栈:
class Solution {
    public int trap(int[] height) {
        //单调栈
        //定义单调(递增)栈存储柱子对应下标
        Deque<Integer> stack = new LinkedList<>();
        //初始化
        stack.push(0);
        int sum = 0;
        //遍历
        for(int i = 1; i < height.length; i++){
            int top = stack.peek();
            if(height[i] < height[top]){//小于栈顶元素直接入栈
                stack.push(i);
            }else if(height[i] == height[top]){//等于栈顶元素则替换栈顶元素
                stack.pop();
                stack.push(i);
            }else{//大于栈头元素高度,则栈头位置可以接雨水
                //每弹出一个元素就计算雨水量
                while(!stack.isEmpty() && height[stack.peek()] < height[i]){
                    int mid = stack.pop();
                    if(!stack.isEmpty()){
                        //雨水量为宽乘高
                        int h = Math.min(height[stack.peek()], height[i]) - height[mid];
                        int w = i - stack.peek() - 1;
                        int rain = h * w;
                        if(rain > 0) sum += rain;
                    }
                }
                //计算结束后弹出栈头元素直到当前元素(高度递增)入栈
                stack.push(i);
            }
        }
        //返回结果
        return sum;
    }
}

84 柱状图中最大的矩形

  • 同上42 接雨水,
  • 双指针:搜索当前柱子左右第一个小于该柱子的下标,时间复杂度O(n2),超时。
  • DP:遍历一遍定义dp数组保存当前柱子左右第一个小于该柱子的下标,最后再遍历求和,面积=当前高度*右边下标-左边下标-1)。
  • 单调栈:定义单调(递减)栈。
  • DP:
class Solution {
    public int largestRectangleArea(int[] heights) {
        //DP
        int[] dpLeft = new int[heights.length];
        int[] dpRight = new int[heights.length];
        //初始化
        dpLeft[0] = -1;
        dpRight[heights.length - 1] = heights.length;
        //分别遍历保存到两个一维数组中(分别存当前列的左右侧第一个小于)
        for(int i = 1; i < heights.length; i++){
            int m = i - 1;
            while(m >= 0 && heights[i] <= heights[m]) m = dpLeft[m];
            dpLeft[i] = m;
        }
        for(int j = heights.length - 2; j >= 0; j--){
            int m = j + 1;
            while(m < heights.length && heights[j] <= heights[m]) m = dpRight[m];
            dpRight[j] = m;
        }
        //递推遍历求和
        int sum = 0;
        for(int i = 0; i < heights.length; i++){
            int h = heights[i];
            int w = dpRight[i] - dpLeft[i] - 1;
            sum = Math.max(sum, h * w);
        }
        //返回结果
        return sum;
    }
}
  • 单调栈:
class Solution {
    public int largestRectangleArea(int[] heights) {
        //单调栈
        //定义单调(递减)栈存储柱子对应下标
        Deque<Integer> stack = new LinkedList<>();
        //初始化
        //这里将数组扩容,两头加0,两边最小能到首尾
        int[] heights2 = new int[heights.length + 2];
        heights2[0] = 0;
        for(int i = 0; i < heights.length; i++){
            heights2[i + 1] = heights[i];
        }
        heights2[heights2.length - 1] = 0;
        stack.push(0);
        int sum = 0;
        //遍历
        for(int i = 1; i < heights2.length; i++){
            int top = stack.peek();
            if(heights2[i] > heights2[top]){//大于栈顶元素直接入栈
                stack.push(i);
            }else if(heights2[i] == heights2[top]){//等于栈顶元素则替换栈顶元素
                stack.pop();
                stack.push(i);
            }else{//小于栈头元素高度,则计算面积
                //每弹出一个元素就计算雨水量
                while(!stack.isEmpty() && heights2[i] < heights2[stack.peek()]){
                    int mid = stack.pop();
                    if(!stack.isEmpty()){
                        //面积为宽乘高
                        int h = heights2[mid];
                        int w = i - stack.peek() - 1;
                        sum = Math.max(sum, h * w);
                    }
                }
                //计算结束后弹出栈头元素直到当前元素(高度递增)入栈
                stack.push(i);
            }
        }
        //返回结果
        return sum;
    }
}

你可能感兴趣的:(leetcode,算法,java)