01 | 数据结构——单调栈问题

概述

解决问题:Next (Greater/Smaller) Element

构建单调递增/递减的栈,寻找下一个符合要求得元素

单调递增/递减

单调递增栈:s.peek()为栈中最小的元素,越往里越大

单调递减栈:s.peek()为栈中最大的元素,越往里越小

Next Greater:由于是寻找下一个更大的元素,需要有位置信息,以及最小的上边界,因此需要单调递增栈

image-20210406142559818

Leetcode

496. Next Greater Element I

You are given two integer arrays nums1 and nums2 both of unique elements, where nums1 is a subset of nums2.

Find all the next greater numbers for nums1's elements in the corresponding places of nums2.

The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. If it does not exist, return -1 for this number.

Example 1:

Input: nums1 = [4,1,2], nums2 = [1,3,4,2]
Output: [-1,3,-1]
Explanation:
For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1.
For number 1 in the first array, the next greater number for it in the second array is 3.
For number 2 in the first array, there is no next greater number for it in the second array, so output -1.

解析

(1)遍历nums2,利用单调s,构建map<val,next_val>

注:由于需要next greater element ,所以nums2应该从右往左遍历

image-20210406144122989

栈顶元素3是栈中最小的元素,同时是与1距离最近的元素,所以后续循环出栈与1作比较:

  • 大于1:就是该栈顶元素
  • 小于等于1:说明此时栈顶元素还不够大,继续找大的

循环变不变量

  • 栈的top元素是距离i最近,且最小的元素
  • 遍历栈直到遇到比它大的元素

(2)根据nums1的值和map定位next_val,得到output

Solution

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int n1 = nums1.length;
        int n2 = nums2.length;
        if(n1 == 0 || n2 == 0){
            return new int[0];
        }
        
        // Get next map nums2
        Map<Integer,Integer> map = new HashMap<>();
        Stack<Integer> s = new Stack<>();
        for(int i=n2-1; i>=0; i--){
            while(!s.isEmpty() && nums2[i] >= s.peek()){
                s.pop();
            }
            map.put(nums2[i], s.isEmpty() ? -1 : s.peek());
            s.push(nums2[i]);
        }
        
        // Get result num1
        int[] res = new int[n1];
        for(int i = 0; i < n1; i++){
            res[i] = map.get(nums1[i]);
        }
        
        return res;
        
    }
}

739. Daily Temperatures

Given a list of daily temperatures T, return a list such that, for each day in the input, tells you how many days you would have to wait until a warmer temperature. If there is no future day for which this is possible, put 0 instead.

For example, given the list of temperatures T = [73, 74, 75, 71, 69, 72, 76, 73], your output should be [1, 1, 4, 2, 1, 1, 0, 0].

Note: The length of temperatures will be in the range [1, 30000]. Each temperature will be an integer in the range [30, 100].

解析

💌next greater number问题,单调递增栈

栈中存放数组T索引

从后至前遍历,循环不变量:

  • 栈的top元素是距离i最近,且最小的元素
  • 遍历栈直到遇到比它大的元素
  • 得到该元素的索引与i的间距,放入结果数组next

Solution

class Solution {
    public int[] dailyTemperatures(int[] T) {
        int n = T.length;
        int[] next = new int[n];
        // stack<index>
        Stack<Integer> s = new Stack<>();
        
        for(int i = n-1; i >= 0; i --){
            while(!s.isEmpty() && T[i] >= T[s.peek()]){
                s.pop();
            }
            next[i] = s.isEmpty() ? 0 : s.peek()-i;
            s.push(i);
        }
        
        return next;
    }
}

503. Next Greater Element II

Given a circular integer array nums (i.e., the next element of nums[nums.length - 1] is nums[0]), return the next greater number for every element in nums.

The next greater number of a number x is the first greater number to its traversing-order next in the array, which means you could search circularly to find its next greater number. If it doesn't exist, return -1 for this number.

Example 1:

Input: nums = [1,2,1]
Output: [2,-1,2]
Explanation: The first 1's next greater number is 2; 
The number 2 can't find next greater number. 
The second 1's next greater number needs to search circularly, which is also 2.

解析

circular integer array问题,有两种解决策略:

  1. 复制数组,模仿环形数组遍历

    image-20210408094035026
  2. 循环上扩充,利用模模仿环形数组遍历

image-20210408094337109

Solution

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

556. Next Greater Element III

Given a positive integer n, find the smallest integer which has exactly the same digits existing in the integer n and is greater in value than n. If no such positive integer exists, return -1.

Note that the returned integer should fit in 32-bit integer, if there is a valid answer but it does not fit in 32-bit integer, return -1.

Example 1:

Input: n = 12
Output: 21

解析

逆序数 7654321 :输出为-1

顺序数 1234567 : 输出为,后两位的交换1234576

乱序数59876:

  1. 生成数组arr = [5,9,8,7,6]
  2. 从后向前遍历,找到第一个比后边小的数5,位置为0
  3. arr [1,4]中找到第一个比5大的数6
  4. 交换5和6的位置

Solution

class Solution {
    public int nextGreaterElement(int n) {
        if(n < 10){
            return -1;
        }
        
        char[] cn = (n + "").toCharArray();
        
        int i,j;
        // cn[i-1] >= cn[i] 59876
        for(i = cn.length-1; i>=1; i--){
            if (cn[i-1] < cn[i]){
                break;
            } 
        } 
        if(i == 0){
            return -1;
        }
        
        // find the smallest char in [i,cn.length-1] that greater than x
        // for(j = cn.length-1; j >= i; j--){
        //     if(cn[j] > x){
        //         break;
        //     }
        // }
        j = binarySearch(cn,i-1);
        
        // swap i-1 j
        char temp = cn[j];
        cn[j] = cn[i-1];
        cn[i-1] = temp;
        
        // reverse
        reverse(cn, i, cn.length-1);
        
        // ack whether val fit in 32-bit integer
        long val = Long.parseLong(new String(cn));
        return val <= Integer.MAX_VALUE ? (int)val : -1;
        
    }
    
    private void reverse(char[] cn, int i, int j){
        while(i < j){
            char temp = cn[i];
            cn[i] = cn[j];
            cn[j] = temp;
            i++;
            j--;
        }
    }
    
    private int binarySearch(char[] cn, int index){
        int right = cn.length ;
        int left = index + 1;
        // [left,right)
        char target = cn[index];
        while(left < right){
            
            int mid = left + (right - left) / 2;
            if(cn[mid] > target){
                left = mid+1;
            }else{
                // cn[mid] <= target
                right = mid;
            }   
        }
        return right - 1;
    }
}
posted @ 2021-04-12 15:56  mhp  阅读(113)  评论(0)    收藏  举报