【栈与队列第二篇】栈的三个经典题目

200 阅读4分钟

文章目录

20. 有效的括号

问题描述

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

解决(新建一个栈,入栈出栈,啥算法都没有)

class Solution {
    public boolean isValid(String s) {

        if (s.length() % 2 == 1) {
            return false;
        }

        char[] charArray=s.toCharArray();

        Stack<Character> stack = new Stack<Character>();
        for (int i=0;i<charArray.length;i++){
            if('('==charArray[i] || '['==charArray[i] || '{'==charArray[i])
                stack.push(charArray[i]);
            else if(')'==charArray[i]){
                if(stack.size() > 0 && '(' == stack.peek())
                    stack.pop();
                else
                    return false;
            }

            else if(']'==charArray[i]){
                if(stack.size() > 0 && '[' == stack.peek())
                    stack.pop();
                else
                    return false;
            }

            else if('}'==charArray[i]){
                if(stack.size() > 0 && '{' == stack.peek())
                    stack.pop();
                else
                    return false;
            }


        }
        if (stack.size()==0)
            return true;
        return false;
    }
}

1047. 删除字符串中的所有相邻重复项

问题描述

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:“abbaca”
输出:“ca”
解释:
例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

提示:

1 <= S.length <= 20000
S 仅由小写英文字母组成。

解题关键:无论是先删除哪一个,都不会影响最终的结果
充分理解题意后,我们可以发现,当字符串中同时有多组相邻重复项时,我们无论是先删除哪一个,都不会影响最终的结果。因此我们可以从左向右顺次处理该字符串。
而消除一对相邻重复项可能会导致新的相邻重复项出现,如从字符串abba 中删除 bb 会导致出现新的相邻重复项 \text{aa}aa 出现。因此我们需要保存当前还未被删除的字符。一种显而易见的数据结构呼之欲出:栈。我们只需要遍历该字符串,如果当前字符和栈顶字符相同,我们就贪心地将其消去,否则就将其入栈即可。

解决

class Solution {
    public String removeDuplicates(String S) {
        char[] charArray = S.toCharArray();
        Stack<Character> stack=new Stack<Character>();
        for (int i=0;i<charArray.length;i++){
            if (stack.size()==0){
                stack.push(charArray[i]);
            }else{
                if (charArray[i] == stack.peek()){
                    stack.pop();
                }else {
                    stack.push(charArray[i]);
                }
            }
        }
        Stack outStack=new Stack();
        while (stack.size()>0){ 
            outStack.push(stack.pop());
        }
        StringBuffer stringBuffer=new StringBuffer();
        while(outStack.size()>0){
            stringBuffer.append(outStack.pop());
        }
        return stringBuffer.toString();
    }
}

栈满足,尾部插入,获取尾部,删除尾部,
但是不满足从头部拿出,所以用了两个栈+一个StringBuffer

用到了两个栈作为一个队列,还不如只用用队列(双端队列),LinkedList

class Solution {
    public String removeDuplicates(String S) {
        char[] charArray = S.toCharArray();
        Deque<Character> deque=new LinkedList<>();  // 要从队列尾巴删除元素,只能用Deque,用Queue无法得到最后一个元素
        for (int i=0;i<charArray.length;i++){
            if (deque.size()==0){
                deque.offer(charArray[i]);   //队列末尾添加元素
            }else{
                if (charArray[i] == deque.getLast()){  // 获取队列最尾巴一个元素
                    deque.removeLast();  // 队列删除最后一个元素
                }else {
                    deque.offer(charArray[i]);
                }
            }
        }
        StringBuffer stringBuffer=new StringBuffer();
        while(deque.size()>0){
            stringBuffer.append(deque.pop());   // 从队列头部,将元素一个个拿出来
        }
        return stringBuffer.toString();
    }
}

双向队列满足,尾部插入,获取尾部,删除尾部,
满足从头部拿出,所以用了一个双向队列+一个StringBuffer

Queue 中 add() 和 offer()都是用来向队列添加一个元素。offer是安全的
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。
Queue 中 remove() 和 poll()都是用来从队列头部删除一个元素。poll是安全的
在队列元素为空的情况下,remove() 方法会抛出NoSuchElementException异常,poll() 方法只会返回 null 。

LinkedList还要转为StringBuffer输出,还不如直接用StringBuffer/StringBuilder

class Solution {
    public String removeDuplicates(String S) {
        char[] charArray = S.toCharArray();
       StringBuffer stringbuffer=new StringBuffer();  // 要从队列尾巴删除元素,只能用Deque,用Queue无法得到最后一个元素
        for (int i=0;i<charArray.length;i++) {
            if (stringbuffer.length() == 0) {
                stringbuffer.append(charArray[i]);   //队列末尾添加元素
            } else {
                if (charArray[i] == stringbuffer.charAt(stringbuffer.length()-1)) {  // 获取队列最尾巴一个元素
                    stringbuffer.deleteCharAt(stringbuffer.length()-1);  // 队列删除最后一个元素
                } else {
                    stringbuffer.append(charArray[i]);  // 尾部插入
                }
            }
        }
        return stringbuffer.toString();  // 直接输出
    }
}

换成stringbuilder也是一样的,这个题目没必要考虑线程安全

class Solution {
    public String removeDuplicates(String S) {
        char[] charArray = S.toCharArray();
       StringBuilder stringbuilder=new StringBuilder();  // 要从队列尾巴删除元素,只能用Deque,用Queue无法得到最后一个元素
        for (int i=0;i<charArray.length;i++) {
            if (stringbuilder.length() == 0) {
                stringbuilder.append(charArray[i]);   //队列末尾添加元素
            } else {
                if (charArray[i] == stringbuilder.charAt(stringbuilder.length()-1)) {  // 获取队列最尾巴一个元素
                    stringbuilder.deleteCharAt(stringbuilder.length()-1);  // 队列删除最后一个元素
                } else {
                    stringbuilder.append(charArray[i]);  // 尾部插入
                }
            }
        }
        return stringbuilder.toString();  // 直接输出
    }
}

String拼接字符串底层还是走StringBuilder,效率太低

小结:栈和队列是LinkedList是阉割版,Deque是完整的LinkedList,自己用字符串/字符数组也可以。
Stack/Queue < LinkedList/Deque < StringBuffer/StringBuilder/charArray

150. 逆波兰表达式(后缀表达式)求值

解决(栈中只存放数字,运算符是出栈信号,所以直接将Stack的泛型设置为Integer)

class Solution {
    public int evalRPN(String[] tokens) {
       Stack<Integer> stack=new Stack<Integer>();
       for (int i=0;i<tokens.length;i++){
           String token=tokens[i];
           if (isNumber(token)){
               stack.push(Integer.parseInt(token));
           }else{
               Integer token1= stack.pop();
               Integer token2= stack.pop();
               if ("+".equals(token)){
                   stack.push(token2 + token1);
               }
               else if ("-".equals(token)){
                   stack.push(token2 - token1);
               }
              else if ("*".equals(token)){
                   stack.push(token2 * token1);
               }
             else  if ("/".equals(token)){
                   stack.push(token2 / token1);
               }
           }
       }
       return stack.pop();
    }

    private boolean isNumber(String token){
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token)); 
    }
}

能用栈就能用LinkedList,就能用StringBuffer

class Solution {
    public int evalRPN(String[] tokens) {
       List<Integer> stack=new LinkedList<>();  // 双向队列
       for (int i=0;i<tokens.length;i++){
           String token=tokens[i];
           if (isNumber(token)){
               stack.add(Integer.parseInt(token));   // 尾部插入,这里的Integer.parseInt不能少,因为tokens是字符串数组,stack是整型栈
           }else{
               Integer token1= stack.remove(stack.size()-1);   // 尾部抛出
               Integer token2= stack.remove(stack.size()-1);
               if ("+".equals(token)){
                   stack.add(token2 + token1);   // 尾部插入
               }
               else if ("-".equals(token)){
                   stack.add(token2 - token1);  // 尾部插入
               }
              else if ("*".equals(token)){
                   stack.add(token2 * token1);  // 尾部插入
               }
             else  if ("/".equals(token)){
                   stack.add(token2 / token1);  // 尾部插入
               }
           }
       }
       return stack.get(0);
    }
    // 数字是罗列不完 0~100 -100~0  无数
    private boolean isNumber(String token){
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }
}

这里不能用StringBuffer了(最后是输出结果,不是输出字符串),只能用Integer数组,存放数字,用数组不好,因为插入和删除元素,而且数组无法确定初始容量