371 阅读2分钟

单调栈就是指栈中的元素必须是按照升序排列的栈,或者是降序排列的栈。升序排列的栈称为递增栈,降序排列的栈称为递减栈.

合法字符串

  1. 字符串中只有字符'('和')'。合法字符串需要括号可以配对。

    比如:输入:"()" 输出:true

    解释:(),()(),(())是合法的。)(,()(,(()是非法的。

    boolean isValid(String s);

   /**
     * 字符串中只有字符'('和')'。合法字符串需要括号可以配对。比如:输入:"()" 输出:true
     * 解释:(),()(),(())是合法的。)(,()(,(()是非法的。
     * 时间复杂度 O(n)  空间复杂度  O(n)
     */
    private static boolean isValid(String s) {
        if (null == s || s.length() == 0 || s.length() % 2 != 0) {
            return false;
        }

        Stack<Character> stack = new Stack<Character>();
        char[] chars = s.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (stack.isEmpty()) {
                stack.push(chars[i]);
                continue;
            }
            if ('(' == stack.peek() && ')' == chars[i]) {
                stack.pop();
            } else {
                stack.push(chars[i]);
            }
        }

        return stack.isEmpty();
    }

优化: 进栈的数据都是 (, 栈中元素都相同时,实际上没有必要使用栈,只需要记录栈中元素个数。

   /**
     * 时间复杂度O(N) 空间复杂度O(1)
     */
    private static boolean isValid1(String s) {
        if (null == s || s.length() == 0 || s.length() % 2 != 0) {
            return false;
        }

        char[] chars = s.toCharArray();
        int num = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '(') {
                num++;
            } else {
                if (num <= 0) {
                    return false;
                }
                num--;
            }
        }
        return num == 0;
    }

变种: 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合
  • 左括号必须以正确的顺序闭合
  • 注意空字符串可被认为是有效字符串
private static boolean isValidC(String s) {
        if (null == s || s.length() == 0 || s.length() % 2 != 0) {
            return false;
        }

        Stack<Character> stack = new Stack<Character>();
        char[] chars = s.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (' ' == chars[i]) {
                return false;
            }

            if ('(' == chars[i] || '{' == chars[i] || '[' == chars[i]) {
                stack.push(chars[i]);
                continue;
            }

            if (stack.isEmpty()) {
                return false;
            }

            if (')' == chars[i]) {
                if ('(' == stack.peek()) {
                    stack.pop();
                } else {
                    return false;
                }
            }

            if ('}' == chars[i]) {
                if ('{' == stack.peek()) {
                    stack.pop();
                } else {
                    return false;
                }
            }

            if (']' == chars[i]) {
                if ('[' == stack.peek()) {
                    stack.pop();
                } else {
                    return false;
                }
            }

        }

        return stack.isEmpty();
    }
  1. 在水中有许多鱼,可以认为这些鱼停放在 x 轴上。再给定两个数组 Size,Dir,Size[i] 表示第 i 条鱼的大小,Dir[i] 表示鱼的方向 (0 表示向左游,1 表示向右游)。这两个数组分别表示鱼的大小和游动的方向,并且两个数组的长度相等。鱼的行为符合以下几个条件:

    • 所有的鱼都同时开始游动,每次按照鱼的方向,都游动一个单位距离;
    • 当方向相对时,大鱼会吃掉小鱼;
    • 鱼的大小都不一样。

    输入:Size = [4, 2, 5, 3, 1], Dir = [1, 1, 0, 0, 0]

    输出:3

    int solution(int[] size, int[] dir)

   /**
     * 时间复杂度 O(n) 空间复杂度O(N)
     */
    private static int solution(int[] size, int[] dir) {

        if (size.length <= 1 || dir.length <= 1 || size.length != dir.length) {
            return size.length;
        }

        Stack<Integer> stack = new Stack<Integer>();
        for (int i = 0; i < size.length; i++) {
            boolean hasEat = false;
            while (!stack.isEmpty() && dir[stack.peek()] == 1 && dir[i] == 0) {
                if (size[stack.peek()] > size[i]) {
                    hasEat = false;
                    break;
                }
                stack.pop();
            }

            if (!hasEat) {
                stack.push(i);
            }
        }
        return stack.size();
    }
  1. 一个整数数组 A,找到每个元素:右边第一个比我小的下标位置,没有则用 -1 表示。

    输入:[5, 2]

    输出:[1, -1]

​ int[] findRightSmall(int[] arr)

    /**
     * 思路 一个数总是想与左边比它大的数进行匹配,匹配到了之后,小的数会消除掉大的数。
     *
     * 时间复杂度 O(N) 空间福再度 O(N)
     * 数组中右边第一个比我小的元素的位置,求解用递增栈。
     *
     */
    private static int[] findRightSmall(int[] arr) {

        int[] resultArr = new int[arr.length];

        Stack<Integer> stack = new Stack<Integer>();
        for (int i = 0; i < arr.length; i++) {
            while(!stack.isEmpty() && arr[stack.peek()] > arr[i]){
                resultArr[stack.pop()] = i;
            }
            stack.push(i);
        }
        while(!stack.isEmpty()){
            resultArr[stack.pop()] =-1;
        }
        return resultArr;
    }
  1. 给定一个正整数数组和 k,要求依次取出 k 个数,输出其中数组的一个子序列,需要满足:1. 长度为 k;2.字典序最小。

    输入:nums = [3,5,2,6], k = 2

    输出:[2,6]

private static int[] findSmallSeq(int[] arr, int k){

        int[] result = new int[k];
        Stack<Integer> stack = new Stack<Integer>();
        for(int i=0; i<arr.length; i++){
            int leftNum = arr.length - i;
            while( !stack.empty() && stack.size()+ leftNum > k && stack.peek()> arr[i]){
                stack.pop();
            }
            stack.push(arr[i]);
        }

        while(stack.size() > k){
            stack.pop();
        }

        for(int i=k-1; i>=0; i--){
            result[i] = stack.peek();
            stack.pop();
        }
        return result;
    }
  1. 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

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

    输出: 10

   /**
     *  需要找到左边第一个比A[i]小的数, leftPos,  也需要找到右边第一个比A[i]小的数。rightPos
     */
    private static int largestRectangleArea1(int[] arr){

        int[] leftSmall = findLeftSmall(arr);
        int[] rightSmall = findRightSmall(arr);
        int result =0;

        for (int i = 0; i < arr.length; i++) {
            int leftPos = leftSmall[i];
            int rightPos = rightSmall[i] == -1 ? arr.length: rightSmall[i];

            int width = rightPos - leftPos -1;
            result = Math.max(result, width * arr[i]);
        }
        return result;
    }


    /**
     * 查询左边小于的数
     */
    private static int[] findLeftSmall(int [] arr){
        int[] resultArr = new int[arr.length];

        Stack<Integer> stack = new Stack<Integer>();
        for (int i = arr.length-1; i > 0; i--) {
            while(!stack.isEmpty() && arr[stack.peek()] > arr[i]){
                resultArr[stack.pop()] = i;
            }
            stack.push(i);
        }
        while(!stack.isEmpty()){
            resultArr[stack.pop()] =-1;
        }
        return resultArr;
    }
    /**
     * 查询右边小于的数
     */
    private static int[] findRightSmall(int[] arr) {

        int[] resultArr = new int[arr.length];

        Stack<Integer> stack = new Stack<Integer>();
        for (int i = 0; i < arr.length; i++) {
            while(!stack.isEmpty() && arr[stack.peek()] > arr[i]){
                resultArr[stack.pop()] = i;
            }
            stack.push(i);
        }
        while(!stack.isEmpty()){
            resultArr[stack.pop()] =-1;
        }
        return resultArr;
    }
    public int largestRectangleArea(int[] A) {
        final int N = A == null ? 0 : A.length;
        int top = 0;
        int[] s = new int[N];
        int ans = 0;
        for (int i = 0; i <= N; i++) {
            final int x = i == N ? -1 : A[i];
            while (top > 0 && A[s[top - 1]] > x) {
                final int height = A[s[--top]];
                final int rightPos = i;
                final int leftPos = top > 0 ? s[top - 1] : -1;
                final int width = rightPos - leftPos - 1;
                final int area = height * width;
                ans = Math.max(ans, area);
            }
            s[top++] = i;
        }
        return ans;
    }