目录
一、如何在Java中使用栈和队列。
1. 操作队列的方法:
(1) 创建队列: Queue<String> queue = new LinkedList<>();
(2) 入队列: queue.offer("×××");
(3) 出队列: String e = queue.poll();
(4) 查看队首元素: String e = queue.peek();
(5) 查看队列是否是空的(empty): boolean b = queue.isEmpty();
(6) 查看队列中的元素个数: int s = queue.size();
(7) 清空队列: queue.clear();
2. 操作栈的方法:
(1) 创建栈: Deque<String> stack = new LinkedList<>();
(2) 压栈: stack.push();
(3) 弹栈: String e = stack.pop();
(4) 查看栈顶元素: String e = stack.peek();
(5) 查看栈是否是空的(empty): boolean b = stack.isEmpty();
(6) 查看栈中的元素个数: int s = stack.size();
(7) 清空栈: stack.clear();
队列:Queue offer / poll / peek
栈: Deque push / pop / peek
二、 关于栈的习题练习
1. 有效的括号
题目:
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1.左括号必须用相同类型的右括号闭合。
2.左括号必须以正确的顺序闭合。
3.每个右括号都有一个对应的相同类型的左括号。
示例:
输入:s = "()"
输出:true
思路:使用栈
栈中存放左括号,将字符串利用 toCharArray() 方法转换为字符数组用来进行遍历比较。遇到左括号就放入栈中,遇到右括号就跟栈的栈顶元素进行比较配对。如果该右括号不能和栈顶元素的左括号匹配(如" [ " 和 " } ")则不满足题目要求,返回 flase;若该右括号匹配成功,则删除栈顶元素,并且进行下一轮的判断。
代码:
class Solution {
public boolean isValid(String s) {
Deque<Character> stack = new LinkedList<>();
char[] array = s.toCharArray();
for (char i : array){
if (i == '(' || i == '[' || i == '{'){
// 如果是左括号,入栈
stack.push(i);
} else {
//代码执行到这里说明是右括号
if (stack.isEmpty()){
//说明遇见了一个没有左括号的右括号,返回 false
return false;
}
//代码执行到这里,说明栈中有左括号,进行比较
//如果匹配失败,返回 false
//如果匹配成功,进行下一轮的判断匹配
char e = stack.pop();
if (!isMatch(e,i)){
return false;
}
}
}
//循环结束,说明每个右括号都有匹配的左括号,但是还要确保栈里没有元素,即所有的左括号和右括号都配对成功
if (stack.isEmpty()){
return true;
} else {
return false;
}
}
public boolean isMatch(char a,char b){
if (a == '(' && b == ')') {
return true;
}
if (a == '[' && b == ']') {
return true;
}
if (a == '{' && b == '}') {
return true;
}
return false;
}
}
2. 逆波兰表达式求值
题目:
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例:
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
思路:利用栈
遇到运算数就入栈,当遇到运算符时,就出栈两个元素进行运算,运算后的结果再接着入栈。
代码:
class Solution {
public int evalRPN(String[] tokens) {
Deque<Long> stack = new LinkedList<>();
for (String i : tokens){
//遍历过程中,要最先判断是运算数还是运算符
if (!isOperator(i)){
//代码执行到这里说明是运算数
//此时 tokens 中的元素都是 String 类型,但是我们需要运算所以要将 String 转换为 long 类型
Long e = Long.parseLong(i);
stack.push(e);
} else {
//代码执行到这里说明是运算符,要连续两次取出栈顶元素跟该运算符进行运算,再将运算后的结果压入栈中
long n2 = stack.pop();
long n1 = stack.pop();
long a = calc(n1,n2,i);
stack.push(a);
}
}
//循环结束,栈中剩下一个元素,即波兰表达式的结果值
long n = stack.pop();
return (int)n;
}
public long calc(long n1,long n2,String i) {
if (i.equals("+")){
return n1 + n2;
}
if (i.equals("-")){
return n1 - n2;
}
if (i.equals("*")){
return n1 * n2;
}
if (i.equals("/")){
return n1 / n2;
}
return -1;
}
public boolean isOperator(String i) {
if (i.equals("+")){
return true;
}
if (i.equals("-")){
return true;
}
if (i.equals("*")){
return true;
}
if (i.equals("/")){
return true;
}
return false;
}
}
3. 栈的压入、弹出序列
题目:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
1. 0<=pushV.length == popV.length <=1000
2. -1000<=pushV[i]<=1000
3. pushV 的所有数字均不相同
示例:
输入:[ 1 , 2 , 3 , 4 , 5 ] , [ 4 , 5 , 3 , 2 , 1 ]
返回值: true
说明: 可以通过
push( 1 ) => push( 2 ) => push( 3 ) => push( 4 ) => pop( ) => push( 5 ) => pop( ) => pop( ) => pop( ) => pop( ) 这样的顺序得到[ 4 , 5 , 3 , 2 , 1 ] 这个序列,返回 true
思路:使用栈
定义两个int类型的数组用来接收数据,但是为了方便操作,再将数组转化为链表。同时创建一个栈来完成判断。
取出第二个序列的第一个元素,判断栈顶元素是否为第二个序列的第一个元素,如果没有这个元素,则将入栈序列中的元素依次压入栈中,直到栈顶元素为第二个序列的第一个元素为止;然后删除第二个序列的该元素,再依次判断剩下的元素。
考虑失败的情况:① 两个列表的长度不相等;② 当要从入栈的链表中取元素时,入栈链表为空。
代码:
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
if (pushA.length != popA.length) {
return false;
}
//将数组转化成链表,便于操作
LinkedList<Integer> pushLinkedList = intArrayToList(pushA);
LinkedList<Integer> popLinkedList = intArrayToList(popA);
//创建栈
Deque<Integer> stack = new LinkedList<>();
//外层循环,循环整个出栈链表,对每个出栈元素进行判断
while (!popLinkedList.isEmpty()) {
int e = popLinkedList.remove(0);
//内层循环,将入栈链表的元素 按照规则 压入栈中
while(stack.isEmpty() || stack.peek() != e) {
if (pushLinkedList.isEmpty()){
return false;
}
stack.push(pushLinkedList.remove(0));
}
//代码执行到这里,说明 栈不为空 并且 栈顶元素 等于 出栈链表的第一个元素,说明这个元素可以 弹出栈
//所以判断完的栈顶元素弹出,进行下一个循环判断
stack.pop();
}
//代码执行到这里说明外层循环顺利结束,没有中途退出
return true;
}
public LinkedList<Integer> intArrayToList(int[] array) {
LinkedList<Integer> list = new LinkedList<>();
for (int i : array){
list.add(i);
}
return list;
}
}
注意:
|| :短路或,所以前后两个语句顺序不可换。前者的条件不成立,后者不再进行判断,直接返回 false ;前者条件成立,后者再进行判断。
如果将本题中的内层循环
stack.isEmpty() || stack.peek() != e改为
stack.peek() != popE || stack.isEmpty()就会报错,因为是短路或,最开始的时候 stack 栈为空(null),无法调用 peek() 方法。所以顺序不可换。