[算法]LeetCode每日一题--739每日温度(Java)

DailyChallenge

739. 每日温度

20200611

难度:中等

题目描述1

根据每日 气温 列表,请重新生成一个列表,对应位置的输出是需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

Solution1

单调栈

维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。

class Solution {
    public int[] dailyTemperatures(int[] T) {
        Deque <Integer> stack = new LinkedList<>();
        int[] ans = new int[T.length];
        for(int i = 0; i < T.length; i++){
            while(!stack.isEmpty() && T[i] > T[stack.peek()]){
                int prevIndex = stack.pop();
                ans[prevIndex] = i-prevIndex;
            }
            stack.push(i);
        }
        return ans;
    }
}

Tips:

Java中栈为什么不用Stack,要用Deque?参考这个链接https://mp.weixin.qq.com/s/Ba8jrULf8NJbENK6WGrVWg

  • Q:在 Java 语言中,不推荐使用 Stack 类?

    是的。实际上,这个不推荐不是某个技术专家或者某个企业的规范标准,而是来自 Java 官方。

  • Q:Java 中的 Stack 类到底怎么了?

    Java 中的 Stack 类,最大的问题是,继承了 Vector 这个类。最大的问题在于,继承使得子类继承了父类的所有公有方法。而 Vector 作为动态数组,是有能力在数组中的任何位置添加或者删除元素的。因此,Stack 继承了 Vector,Stack 也有这样的能力!会破坏栈这种数据结构的封装

  • Q:问题出在哪里?

    Stack 和 Vector 之间的关系,不应该是继承关系,而应该是组合关系(composition)。继承关系描述的是 is-a 的关系,即“是一个”的关系。而组合关系描述的是 has-a 的关系,即“有一个”的关系。

  • Q:为什么使用接口?

    接口最大的意义之一,就是做了更高层次的抽象:只定义了一个类应该满足哪些方法,而对具体的实现方式不做限制。Deque 是双端队列的意思。所谓的双端队列,就是能在线性数据结构的两段,进行插入和删除操作。

**其他相似的单调栈题目:**单调栈一般用来处理一种典型的问题,即 Next Greater Element

496. 下一个更大元素 I

难度:简单

题目描述2

给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
    对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。
    对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。
    对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。

示例 2:

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
    对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
    对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

提示:

  1. nums1nums2中所有元素是唯一的。
  2. nums1nums2 的数组大小都不超过1000。

Solution2

单调栈

这里栈里面放的是值

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Deque<Integer> stack = new LinkedList<>();
        Map<Integer, Integer> map = new HashMap<>();
        // 对nums2 中的每一个元素,求出其下一个更大的元素
        for(int i = 0; i < nums2.length; i++){
            while(!stack.isEmpty() && nums2[i] > stack.peek()){
                map.put(stack.pop(), nums2[i]);
            }
            stack.push(nums2[i]);
        }
        while(!stack.empty()){
            map.put(stack.pop(), -1);
        }
        //在map中找到nums1中每个元素的下一个更大元素
        for(int i = 0; i < nums1.length; i++){
            nums1[i] = map.get(nums1[i]);
        }
        return nums1;
    }
}

也可以从后往前扫描。排队看后面第一个比自己高的,从后往前遍历,可以很好地避免不知道后面是什么情况。

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Deque<Integer> stack = new LinkedList<>();
        Map<Integer, Integer> map = new HashMap<>();
        int[] ans = new int[nums1.length];
        for(int i = nums2.length-1; i >= 0; i--){
            while(!stack.isEmpty() && stack.peek() <= nums2[i]){
                stack.pop();//个子矮的走开
            }
            map.put(nums2[i], stack.isEmpty() ? -1 : stack.peek());
            stack.push(nums2[i]);
        }
        for(int i = 0; i < nums1.length; i++){
            nums1[i] = map.get(nums1[i]);
        }
        return nums1;
    }
}

503. 下一个更大元素 II

难度:中等

题目描述3

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。

示例 1:

输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

Solution3

是循环数组,所以遍历两遍数组,效果等同于循环。同样用单调栈

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        Deque<Integer> stack = new LinkedList<>();
        int[] ans = new int[nums.length];
        for(int i = 2*n-1; i >= 0; i--){
            while(!stack.isEmpty() && stack.peek() <= nums[i % n]){
                stack.pop();
            }
            ans[i % n] = stack.isEmpty() ? -1 : stack.peek();
            stack.push(nums[i % n]);
        }
        return ans;  
    }
}

由于本题中未规定数组中元素无重复,所以为了唯一识别一个元素,栈中需要保存数组的索引,而不是数组元素的值。(虽然上面的代码也能通过所有测试用例)

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        Deque<Integer> stack = new LinkedList<>();
        int[] ans = new int[nums.length];
        for(int i = 2*n-1; i >= 0; i--){
            while(!stack.isEmpty() && nums[stack.peek()] <= nums[i % n]){
                stack.pop();
            }
            ans[i % n] = stack.isEmpty() ? -1 : nums[stack.peek()];
            stack.push(i % n);
        }
        return ans;  
    }
}

84. 柱状图中最大的矩形

难度:困难

题目描述4

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

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

示例:

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

Solution4

单调栈 + 加入哨兵

遍历的时候,记录下标,如果当前的高度比它之前的高度严格小于的时候,就可以直接确定之前的那个高的柱形的最大矩形的面积。

维护一个单调栈,栈中总是保存递增元素的索引,当遇到比栈顶元素小的元素时,将栈顶元素依次出栈,每次都计算栈中的bar能围成的面积,直到栈顶元素小于当前元素就停止出栈。

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        if(n == 0) return 0;
        if(n == 1) return heights[0];
        int res = 0;
        // 放入左右两个哨兵
        int[] newHeights = new int[n + 2];
        newHeights[0] = 0;
        System.arraycopy(heights, 0, newHeights, 1, n);
        newHeights[n+1] = 0;
        n += 2;
        heights = newHeights;

        Deque<Integer> stack = new ArrayDeque<>(n);
        stack.addLast(0);//放入哨兵
        for(int i = 1; i < n; i++){
            while(heights[i] < heights[stack.peekLast()]){
                int curHeight = heights[stack.pollLast()];
                int curWidth = i - stack.peekLast() - 1;
                res = Math.max(res, curHeight * curWidth);
            }
            stack.addLast(i);
        }
        return res;
    }
}

我的公众号: 暂时每日分享LeetCode,我不断学习的过程中,公众号也在不断充实,欢迎大家扫码关注。
在这里插入图片描

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值