Algorithm-Monotonic Stack Problem Collection

A monotonic stack, as its name implies, is a monotonically increasing or monotonically decreasing stack. Although it is very simple, it is indeed an advanced data structure.

The article I wrote before

The algorithm-skyscraper problem is optimized using a monotonic stack.

Algorithm-the maximum sliding window is to maintain a monotonic queue to solve the problem

With the help of monotonic stack or monotonic queue, we can optimize the time complexity of some algorithms. Here are some more monotonic stack problems

1. Remove the K digits and keep the remaining digits to a minimum

This question is also the original question of the second question of Huawei 2020-04-29

402. Remove K digits

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:

num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
示例 1 :

输入: num = "1432219", k = 3
输出: "1219"
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。
示例 2 :

输入: num = "10200", k = 1
输出: "200"
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
示例 3 :

输入: num = "10", k = 2
输出: "0"
解释: 从原数字移除所有的数字,剩余为空就是0。

Problem-solving ideas:

First of all, intuitively speaking, we need to remove the largest element in the number so that the number can be made smaller. However, taking the following number as an example, can removing the largest element definitely make the number smaller?

3214

Obviously, when k=1, the number we remove should be 3, not 4, so that we can get the smallest number.

Why is removing 4 not the smallest? Because we also need to consider the position of the removed element, in short, the earlier the element has the greater influence.

We can solve this problem with a monotonic stack.

1. Traverse each element and maintain a monotonically increasing stack containing the current node. Elements larger than the current node will be popped.
2. In 1, we only need to remove k elements, that is, when the number of deleted elements exceeds k, there is no need to maintain the monotonic stack.
3. In 2, after the traversal is completed, the removed elements may not reach k, that is, the elements behind the monotonic stack need to be popped.
4. Pop the stack element obtained in 3.
What is obtained in 5 and 4 is the reverse order of the data. We delete the elements whose positions are continuously 0 and then reverse the order to obtain the minimum value.

    public String removeKdigits(String num, int k) {
    
    
        if(num==null||k>=num.length()){
    
    
            return "0";
        }
        Stack<Character> stack=new Stack<>();
        for(int i=0;i<num.length();i++){
    
    
            char c=num.charAt(i);
            while(k>0&&stack.size()>0&&stack.peek()>c){
    
    
                stack.pop();
                k--;
            }
            stack.push(c);
        }
        while(k-->0){
    
    
            stack.pop();
        }
        StringBuilder sb=new StringBuilder();
        while(stack.size()>0){
    
    
            sb.append(stack.pop());
        }
        while(sb.length()>1&&sb.charAt(sb.length()-1)=='0'){
    
    
            sb.deleteCharAt(sb.length()-1);
        }
        return sb.reverse().toString();
    }

2. Remove K digits and keep the remaining digits to the maximum

This question was given by the interviewer when I was facing the byte. I gave a greedy algorithm that kept the maximum value each time, but the interviewer still hung me because I couldn't optimize.

Similar to 1, this problem only needs to maintain a monotonically decreasing stack. So we only need to change the symbol

    public static String removeKdigits(String num, int k) {
    
    
        if (num == null || k >= num.length()) {
    
    
            return "0";
        }
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < num.length(); i++) {
    
    
            char c = num.charAt(i);
            //维护一个以本节点为栈顶的单调递减栈(即从左到右删掉前面元素小于后面元素的情况)
            while (!stack.isEmpty() && stack.peek() < c && k > 0) {
    
    
                stack.pop();
                k--;
            }
            stack.push(c);
        }
        //如果没有删完,继续删掉后面的一些元素
        while (k > 0) {
    
    
            stack.pop();
            k--;
        }
        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) {
    
    
            sb.append(stack.pop());
        }
        while (sb.length() > 1 && sb.charAt(sb.length() - 1) == '0') {
    
    
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.reverse().toString();
    }

3. Remove duplicate letters and minimize the dictionary order

316. Remove duplicate letters

给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。
需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

示例 1:

输入: "bcabc"
输出: "abc"
示例 2:

输入: "cbacdcbc"
输出: "acdb"

This problem can also be solved with a monotonic stack, and even the code of the first problem can be directly put on... Here are two solutions

1. Count the number of repeated letters k, and use the method of title 1 to get the smallest dictionary order.
2. Whenever a new letter is encountered, determine the size of the new letter and the top element of the stack. If the new letter is smaller than the top element of the stack, and the top element of the stack is After the string appears again, pop the top element of the current stack, and finally push the current element onto the stack

    public String removeDuplicateLetters(String s) {
    
    
        Stack<Character> stack=new Stack<>();
        for(int i=0;i<s.length();i++){
    
    
            char c=s.charAt(i);
            if(!stack.contains(c)){
    
    
                while(!stack.isEmpty()&&stack.peek()>c&&s.indexOf(stack.peek(),i)!=-1){
    
    
                    stack.pop();
                }
                stack.push(c);
            }
        }
        StringBuilder sb=new StringBuilder();
        while(!stack.isEmpty()){
    
    
            sb.append(stack.pop());
        }
        return sb.reverse().toString();
    }

It is not difficult to see that the time complexity of the solution given above may degenerate to N^2, because the array may be traversed when looking for the last position of the top element on the stack.
In addition, when judging whether there is an element in the stack, although the time complexity is O(1), this O(1) may be O(26), so we can also make a fuss from this.

What data structure has a real time complexity of O(1)? The answer is self-evident, it is HashMap!

However, even if the time complexity of HashMap lookup is O(1), the cost of calculating the index and the call stack is also a non-negligible place. Since the number of letters is limited, we can use an array to implement the hash table ourselves.

Give everyone a way to beat 96.74% of users

执行用时 :
3 ms, 在所有 Java 提交中击败了96.74%的用户
内存消耗 :39.4 MB, 在所有 Java 提交中击败了16.67%
的用户

We can use one array to store the last position of the element and
another array to store whether there is an element in the stack

    public String removeDuplicateLetters(String s) {
    
    
        int[] map=new int[128];
        char cs[]=s.toCharArray();
        for(int i=cs.length-1;i>=0;i--){
    
    
            if(map[cs[i]]==0){
    
    
                map[cs[i]]=i;//标注元素最后出现的位置
            }
        }
        int[] mark=new int[128];//标注是不是栈里面的新元素
        Stack<Character> stack=new Stack<>();
        for (int i=0;i<cs.length;i++){
    
    
            char c=cs[i];
            if(mark[c]==0){
    
    
                //当前栈顶元素大于新元素,并且栈顶元素最后出现的位置大于当前位置,那么弹出栈顶元素
                while (!stack.isEmpty()&&stack.peek()>c&&map[stack.peek()]>i){
    
    
                    mark[stack.peek()]=0;//弹出了,不再是新元素
                    stack.pop();
                }
                stack.push(c);
                mark[c]=1;//标注已入栈
            }
        }
        StringBuilder sb=new StringBuilder();
        while (stack.size()>0){
    
    
            sb.append(stack.pop());
        }
        return sb.reverse().toString();
    }

This method is basically optimal, and the real control of the time complexity is O(N).
The further optimization may be that we use arrays and pointers to implement a stack ourselves to speed up access. Adjusted the size of the hash table that I realized to 26, and repaired these corners.

Guess you like

Origin blog.csdn.net/qq_23594799/article/details/105867711