单调栈java算法

比如说,输入一个数组 nums = [2,1,2,4,3],你返回数组 [4,2,4,-1,-1]

解释:第一个 2 后面比 2 大的数是 4; 1 后面比 1 大的数是 2;第二个 2 后面比 2 大的数是 4; 4 后面没有比 4 大的数,填 -1;3 后面没有比 3 大的数,填 -1。

这道题的暴力解法很好想到,就是对每个元素后面都进行扫描,找到第一个更大的元素就行了。但是暴力解法的时间复杂度是 O(n^2)

这个问题可以这样抽象思考:把数组的元素想象成并列站立的人,元素大小想象成人的身高。这些人面对你站成一列,如何求元素「2」的 Next Greater Number 呢?

很简单,如果能够看到元素「2」,那么他后面可见的第一个人就是「2」的 Next Greater Number,因为比「2」小的元素身高不够,都被「2」挡住了,第一个露出来的就是答案。

单调栈java算法_第1张图片

代码如下:

 public int [] nextGreaterElement(int[] nums){
           Stack stack=new Stack<>();
           int [] res=new int[nums.length];
           int n=nums.length;
          for (int i=n-1;i>=0;i--){
              while (!stack.isEmpty()&&stack.peek()<=nums[i]){
                  stack.pop();
              }
              res[i]=stack.empty()?-1:stack.peek();
              stack.push(nums[i]);
          }
          return res;
       }

 

这就是单调队列解决问题的模板。for 循环要从后往前扫描元素,因为我们借助的是栈的结构,倒着入栈,其实是正着出栈。while 循环是把两个「个子高」元素之间的元素排除,因为他们的存在没有意义,前面挡着个「更高」的元素,所以他们不可能被作为后续进来的元素的 Next Great Number 了。

这个算法的时间复杂度不是那么直观,如果你看到 for 循环嵌套 while 循环,可能认为这个算法的复杂度也是 O(n^2),但是实际上这个算法的复杂度只有 O(n)

分析它的时间复杂度,要从整体来看:总共有 n 个元素,每个元素都被 push 入栈了一次,而最多会被 pop 一次,没有任何冗余操作。所以总的计算规模是和元素规模 n 成正比的,也就是 O(n) 的复杂度。

单调栈java算法_第2张图片

 

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
      Stack stack=new Stack<>();
           HashMap hashMap=new HashMap<>();

           int[] res=new int[nums1.length];
           for (int i : nums2) {
               while (!stack.isEmpty()&&stack.peek()

单调栈的使用技巧差不多了,来一个简单的变形,力扣第 739 题「 每日温度」:给你一个数组 T,这个数组存放的是近几天的天气气温,你返回一个等长的数组,计算:对于每一天,你还要至少等多少天才能等到一个更暖和的气温;如果等不到那一天,填 0

比如说给你输入 T = [73,74,75,71,69,76],你返回 [1,1,3,2,1,0]

解释:第一天 73 华氏度,第二天 74 华氏度,比 73 大,所以对于第一天,只要等一天就能等到一个更暖和的气温,后面的同理。

这个问题本质上也是找 Next Greater Number,只不过现在不是问你 Next Greater Number 是多少,而是问你当前距离 Next Greater Number 的距离而已。

相同的思路,直接调用单调栈的算法模板,稍作改动就可以,直接上代码吧:

  public int[] dailyTemperatures(int[] temperatures) {
          Stack stack=new Stack<>();
          int [] res=new int[temperatures.length];
           for (int i=temperatures.length-1;i>=0;i--){
               while (!stack.isEmpty()&&temperatures[stack.peek()]<=temperatures[i]){
                   stack.pop();
               }
               res[i]= stack.empty() ? 0 : (stack.peek() - i);
               stack.push(i);
           }
           return res;
    }

如何处理环形数组

同样是 Next Greater Number,现在假设给你的数组是个环形的,如何处理?力扣第 503 题「 下一个更大元素 II」就是这个问题:

比如输入一个数组 [2,1,2,4,3],你返回数组 [4,2,4,-1,4]。拥有了环形属性,最后一个元素 3 绕了一圈后找到了比自己大的元素 4

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

你可能感兴趣的:(数据结构与算法,算法,leetcode)