补 day11算法打卡 | 栈与队列part02 | 20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值

1.1Deque的三种用途

  • 普通队列(一端进另一端出): Queue queue = new LinkedList()或Deque deque = new LinkedList()
  • 双端队列(两端都可进出):Deque deque = new LinkedList()
  • :Deque deque = new LinkedList()(栈java.util.Stack类已经过时,Java官方推荐使用java.util.Deque替代Stack使用。Deque栈的操作方法:push()、pop()、peek()

原文链接:https://blog.csdn.net/qq_35091353/article/details/116783260

补 day11算法打卡 | 栈与队列part02 | 20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值_第1张图片

1.2 栈的两种实现方法

Deque<Integer> stack = new LinkedList<Integer>();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(stack.pop());		// 3
System.out.println(stack.peek());		// 2
System.out.println(stack.peek());		// 2

Stack<Integer> stack2 = new Stack<>();
stack2.push(4);
stack2.push(5);
stack2.push(6);
System.out.println(stack2.pop());		// 6
System.out.println(stack2.peek());		// 5
System.out.println(stack2.peek());		// 4

1.3 Deque与Stack对应的接口

补 day11算法打卡 | 栈与队列part02 | 20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值_第2张图片

20. 有效的括号

括号匹配是使用解决的经典问题。
栈结构的特殊性,非常适合做对称匹配类的题目

写代码之前要分析好有哪几种不匹配的情况

  1. 字符串里左方向的括号多余了 ,所以不匹配。
  2. 括号没有多余,但是 括号的类型没有匹配上。
  3. 字符串里右方向的括号多余了,所以不匹配。

代码判断不匹配:

  1. 第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false。
  2. 第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false。
  3. 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false。

代码判段匹配:
那么什么时候说明左括号和右括号全都匹配了呢:
就是字符串遍历完之后,栈是空的,就说明全都匹配了。

class Solution {
    public boolean isValid(String s) {
        Deque<Character> deque = new LinkedList<>();
        char[] ch = s.toCharArray();

        for (int i = 0; i<s.length(); i++) {
            // 碰到字符串里的左括号 就把对应的右括号入栈
            if (ch[i] == '(') {
                deque.push(')');
            }else if (ch[i] == '[') {
                deque.push(']');
            }else if (ch[i] == '{') {
                deque.push('}');
            }else if (deque.isEmpty() || deque.peek() != ch[i]) {
                // 运行到这里说明:ch[i] 一定是右括号
                // 如果第三种情况,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
                // 如果第二种情况,发现栈里第一个和ch[i]不相等,没有我们要匹配的字符。所以return false
                return false;
            }else {
                // 一定是右括号里 排除了 deque.peek() != ch[i] 不相等的情况,只剩下相等的情况
                // 相等就出栈
                deque.pop();
            }
        }
        // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,
        // 说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
        return deque.isEmpty();
    }
}
  1. 用toCharArray() 转换为字符数组

  2. 为什么使用LinkedList?

    这段代码中使用 LinkedList 是因为 Deque 接口的实现类 LinkedList 是一种双向链表,它可以同时实现队列和栈的功能,而在这段代码中需要同时使用队列和栈的功能。
    在该代码中,使用 LinkedList 创建了一个 Deque 对象 deque,用于存储字符。通过 deque 实现了栈的功能,使用 push 方法将字符入栈,使用 pop 方法将字符出栈。同时,LinkedList 也可以当作队列使用,通过 peek 方法获取队列头部的字符。由于这段代码需要同时实现栈和队列的功能,因此选择了 LinkedList 作为实现类

  3. 为什么不用ArrayList?

    ArrayList 是一个基于数组实现的动态数组,它提供了随机访问元素的能力,并且具有动态扩展和收缩的能力。相比于 LinkedListArrayList 在随机访问元素时具有更好的性能,而在插入和删除元素时相对较慢。

    以下是一些适合使用 ArrayList 的情况:

    1. 当需要高效的随机访问元素,例如根据索引快速获取元素或更新元素。
    2. 当对列表进行频繁的遍历操作,而不涉及频繁的插入和删除操作。
    3. 当需要动态地调整列表的大小,根据需要添加或删除元素。
  4. 总结:
    总而言之,当需要高效的随机访问和动态调整大小的列表时,ArrayList 是一个合适的选择。
    当需要频繁进行插入和删除操作,特别是在列表的中间位置时,可以考虑使用 LinkedList

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

class Solution {
    public String removeDuplicates(String s) {
        // ArrayDeque会比LinkedList在除了删除元素这一点外会快一点
        // 参考:https://stackoverflow.com/questions/6163166/why-is-arraydeque-better-than-linkedlis
        // Linkedlist deque = new Linkedlist<>(); 会报错
        ArrayDeque<Character> deque = new ArrayDeque<>();
        char ch;
        for (int i = 0; i < s.length(); i++) {
            ch = s.charAt(i);
            // 如果栈是空 或者 栈顶元素不相等
            // 在一开始的时候,和 aabb,abba的时候都会存在栈为空的情况。等同于栈顶元素不相等
            if (deque.isEmpty() || deque.peek() != ch) {
                deque.push(ch);
            } else {
                deque.pop();
            }
        }
		// 通过定义一个空字符,实现栈内元素的反转
        String str = "";
        while (!deque.isEmpty()) {
            str = deque.pop() + str;
        }

        return str;
    }
}
  1. 使用 Linkedlist 会报错不知道为什么。
  2. // 如果栈是空 或者 栈顶元素不相等,将字符串元素入栈
      // 在一开始的时候,和 aabb,abba的时候都会存在栈为空的情况。等同于栈顶元素不相等
    

150. 逆波兰表达式求值 (中等、不会)

逆波兰表达式相当于是二叉树中的后序遍历。 大家可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树。
知道逆波兰表达式是用后序遍历的方式把二叉树序列化了,就可以了
相邻字符的消除:
符合如下条件的 相邻字符消除
2 + 1 = 3
两个数字一个操作符,也做一个“消除操作”,然后把结果入栈。
2,1遇到 ’+‘,把2,1出栈(消除),把运算结果入栈 。

最后的结果存在的栈里。

class Solution {
    public int evalRPN(String[] tokens) {
        // 使用双端队列创建一个栈
        // 因为只放数字不放运算符,所以是Integer类型
        Deque<Integer> stack = new LinkedList();
        for (int i = 0; i < tokens.length; i++) {
            // 遇到运算符 就出栈两个元素,跟这个运算符进行运算
 //           if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
           if (tokens[i].equals("+") || tokens[i].equals("-") || tokens[i].equals("*") || tokens[i].equals("/")) {
                int num1 = stack.peek();
                stack.pop();
                int num2 = stack.peek();
                stack.pop();
                
                // "-" 和 "/" 需要特别注意顺序
                if (tokens[i].equals("+")) {
                    stack.push(num1 + num2);
                } else if (tokens[i].equals("-")) {
                    stack.push(-num1 + num2);
                } else if (tokens[i].equals("*")) {
                    stack.push(num1 * num2);
                } else if (tokens[i].equals("/")) {
                    stack.push(num2 / num1);
                }
            // 如果不是运算符就进栈,然后转换为int
            // 使用Integer.valueOf(String)方法
            } else {
                stack.push(Integer.valueOf(tokens[i]));
            }
        }

        return stack.pop();
    }
}
  1. Deque stack = new LinkedList(); 使用双端队列创建一个栈,因为只放数字不放运算符,所以是Integer类型。
  2. tokens[i] == “+” 会出错,用 tokens[i].equals(“+”)。
  3. “-” 和 “/” 需要特别注意顺序
    后序遍历的顺序是“左右中”,所以“左”先被入栈,出栈时候就是num2,应该是num2 - num1;除法“/” 也是同理。
  4. stack.push(tokens[i]); 会报错。
    stack.push(Integer.valueOf(tokens[i])); 将string类型的数字转换为int
    牢记常用函数:Integer.valueOf()

你可能感兴趣的:(算法,java)