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
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对应的接口
20. 有效的括号
括号匹配是使用栈解决的经典问题。
栈结构的特殊性,非常适合做对称匹配类的题目
写代码之前要分析好有哪几种不匹配的情况:
- 字符串里左方向的括号多余了 ,所以不匹配。
- 括号没有多余,但是 括号的类型没有匹配上。
- 字符串里右方向的括号多余了,所以不匹配。
代码判断不匹配:
- 第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false。
- 第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false。
- 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号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();
}
}
-
用toCharArray() 转换为字符数组
-
为什么使用LinkedList?
这段代码中使用
LinkedList
是因为 Deque 接口的实现类LinkedList
是一种双向链表,它可以同时实现队列和栈的功能,而在这段代码中需要同时使用队列和栈的功能。
在该代码中,使用LinkedList
创建了一个Deque 对象 deque
,用于存储字符。通过deque
实现了栈的功能,使用push
方法将字符入栈,使用pop
方法将字符出栈。同时,LinkedList
也可以当作队列使用,通过 peek 方法获取队列头部的字符。由于这段代码需要同时实现栈和队列的功能,因此选择了LinkedList
作为实现类 -
为什么不用ArrayList?
ArrayList
是一个基于数组实现的动态数组,它提供了随机访问元素的能力,并且具有动态扩展和收缩的能力。相比于LinkedList
,ArrayList
在随机访问元素时具有更好的性能,而在插入和删除元素时相对较慢。以下是一些适合使用
ArrayList
的情况:- 当需要高效的随机访问元素,例如根据索引快速获取元素或更新元素。
- 当对列表进行频繁的遍历操作,而不涉及频繁的插入和删除操作。
- 当需要动态地调整列表的大小,根据需要添加或删除元素。
-
总结:
总而言之,当需要高效的随机访问和动态调整大小的列表时,ArrayList
是一个合适的选择。
当需要频繁进行插入和删除操作,特别是在列表的中间位置时,可以考虑使用LinkedList
。
1047. 删除字符串中的所有相邻重复项
class Solution {
public String removeDuplicates(String s) {
// ArrayDeque会比LinkedList在除了删除元素这一点外会快一点
// 参考:https://stackoverflow.com/questions/6163166/why-is-arraydeque-better-than-linkedlis
// Linkedlist<Character> 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;
}
}
- 使用 Linkedlist 会报错不知道为什么。
-
// 如果栈是空 或者 栈顶元素不相等,将字符串元素入栈 // 在一开始的时候,和 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();
}
}
- Deque stack = new LinkedList(); 使用双端队列创建一个栈,因为只放数字不放运算符,所以是Integer类型。
- tokens[i] == “+” 会出错,用 tokens[i].equals(“+”)。
- “-” 和 “/” 需要特别注意顺序
后序遍历的顺序是“左右中”,所以“左”先被入栈,出栈时候就是num2,应该是num2 - num1;除法“/” 也是同理。 - stack.push(tokens[i]); 会报错。
stack.push(Integer.valueOf(tokens[i])); 将string类型的数字转换为int
牢记常用函数:Integer.valueOf()