数据结构算法(单调栈)

一.单调栈的定义

        单调栈本质上还是栈,表示的是一种特殊的数据结构,用来解决某类问题。单调栈,即存放在该栈中的元素是按照单调递增或单调递减的顺序存放。当有新的元素要进栈时,栈在调整的过程中,会打破原有的单调性,并重新建立单调性。所以单调栈的时间复杂度是O(n)

二.单调栈的性质

1.栈内的元素保持单调性。

2. 元素在入栈前,会将破坏栈内元素单调性的元素进行出栈。

三.题目详解

        掌握某个知识点的最佳方法就是在运用到题目中,并能做到基本理解。将某个知识点的解题思想,转化为自己解决某类问题的新思路。

1.下一个更大的元素(496. 下一个更大元素 I - 力扣(LeetCode))

解决该题可以用暴力法,也就是我们用最常见最普通的方法去解决

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
         // 入参判断
        if(nums1 == null || nums2 == null || nums1.length == 0){
            return null;
        }
        int[] res = new int[nums1.length];
        Arrays.fill(res, -1);
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length; j++) {
                if(nums1[i] == nums2[j]){
                    while( j +1 < nums2.length){
                        if(nums2[j + 1] > nums1[i]) {
                            res[i] = nums2[j + 1];
                            break;
                        }else{
                            j++;
                        }
                    }
                }
            }
        }
        return res;
    }
}

上述方法的时间复杂度为O(n^2)。接下来,重点说的是用单调栈去解决该题。

思路:首先要明白,该如何去查找一个元素下一个比他大的元素,并记录结果。

可以先将要比较的元素的下一个元素放到栈中,当进行比较的时候直接用当前元素和栈顶元素进行比较。这样做的目的,在方便比较的同时,避免了对数组进行过多的操作,并且在出栈时也方便了许多。可以创建一个数组用来存放比较的结果。

每次栈内存放的元素,或者说是栈顶元素,一定是下一次要进行比较的元素的下一个元素。只要当前元素小于栈顶元素,说明栈顶元素就是该元素的下一个最大的元素。

最后在对两个数组进行遍历,找到目标数字在查找数组中的位置,直接获取该元素在存放比较的结果中的数组中的值,就是最终的结果。

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        // 入参判断
        if (nums1 == null || nums2 == null || nums1.length == 0) {
            return null;
        }
        // 用来记录在nums数组中,在当前元素的后方是否有比当前元素大的元素
        int[] result = new int[nums2.length];
        Stack stack = new Stack<>();
        // 遍历数组(因为是寻找下一个更大的元素,所以从数组的最后方开始遍历,将当下要比较的元素的下一个元素先入栈)
        for (int i = nums2.length - 1; i >= 0; i--) {
            // 如果当前元素大于栈顶元素,就将栈顶元素出栈
            while (!stack.isEmpty() && stack.peek() <= nums2[i]) {
                stack.pop();
            }
            result[i] = stack.isEmpty() ? -1 : stack.peek(); //如果没有比当前元素的大的元素,存放-1,否则将比当前元素大的元素存放到该数组中
            stack.push(nums2[i]); //入栈
        }
        //创建该数组用于记录最终比较的结果
        int [] res = new int[nums1.length];
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length;  j++) {
                // 遍历找到nums数组中的nums1,并将上一步记录在数组中的值赋给新数组,对应的就是该元素下一个最大的元素
                if(nums1[i] == nums2[j]){
                    res[i] = result[j];
                    break;
                }
            }
        }
        return res;
    }
}
public static void main(String[] args) {
    int[] nums1 = {4, 1, 2};
    int[] nums2 = {1, 3, 4, 2};
    Demo demo = new Demo();
    System.out.println(Arrays.toString(demo.nextGreaterElement(nums1,nums2)));
}

数据结构算法(单调栈)_第1张图片 数据结构算法(单调栈)_第2张图片

 2.去除重复字母(316. 去除重复字母 - 力扣(LeetCode))

该题同样也是利用单调栈来解决,还涉及到了字典序的概念。

字典序:通常是指按照字典中字符出现的先后顺序排列的 这个概念最初用于英文单词在字典中的排列顺序。

该题总结下来大概三点:

① 去重,去除字符串中的重复字符。

② 在去重后的字符串中不能打乱原字符串中每个字符的相对顺序。

③ 在上述的两条要求中,要求最后返回的结果字典序最小

例如:一个字符串 "babc" ,在去重后有两种结果,"bac"和"abc",因为"abc"的字典序更小,所以最终的结果应该是"abc"。

代码如下:

class Solution {
    public String removeDuplicateLetters(String s) {
        StringBuilder sb = new StringBuilder(); //用来做输出
        // 入参判断
        if (s == null || s.length() < 2) {
            return s;
        }
        Stack stack = new Stack<>();
        // 输入字符为对应的ASCII值(0~256)
        int[] count = new int[256];
        // 统计每个字符出现的次数,并记录在count数组中
        for (int i = 0; i < s.length(); i++) {
            count[s.charAt(i)]++;
        }
        // boolean数组初始值为false,当有字符入栈时,就把该字符标记为true
        boolean[] inStack = new boolean[256];
        for (int i = 0; i < s.length(); i++) {
            // 遍历过某个字符,就将该字符的数量-1
            count[s.charAt(i)]--;
            // 某个字符在boolean数组中为true,说明该字符已入栈,直接continue跳出本次循环
            if (inStack[s.charAt(i)]) {
                continue;
            }
            // 判断是否要进行出栈
            while (!stack.isEmpty() && stack.peek() > s.charAt(i)) {
                // 如果满足该条件,说明后面再没有该元素,就不能进行出栈
                if (count[stack.peek()] == 0) {
                    break;
                } else {
                    // 否则出栈,并把该字符标记为false
                    inStack[stack.pop()] = false;
                }
            }
            // 如果不满足if(inStack[s.charAt(i)]),就将该字符入栈,并把其标记为true
            stack.push(s.charAt(i));
            inStack[s.charAt(i)] = true;

        }
        // 将栈中的元素附加到此序列
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.reverse().toString(); //将该字符串序列反转并返回一个字符串对象
    }
}

 

 

你可能感兴趣的:(数据结构,java,开发语言,算法)