目录
栈
栈的相关问题
不可能的出栈顺序:
前、中、后缀表达式
栈的常用方法
栈的相关OJ题
逆波兰表达式
验证栈序列
有效的括号
最小栈
模拟实现栈
数据结构就是用来存储数据的一种方式。
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
了解了栈后,有时可能会遇到这样一个面试问题:
我们已经知道了栈是后进先出的,但我们不要忽视一点,入栈后是可以出栈的,不必要入完后才出,也就数说可以一边入栈一边出栈,所以本题答案选C。
给你一个中缀表达式,把它转化为前缀表达式或后缀表达式,注:我们平时写的算式是中缀表达式。
笔试题:按运算顺序为每个运算添上一个括号,再将运算符移到加的括号后边,最后删掉括号即可。
中缀转后缀:
同理,将运算符移到最近的括号前面即使前缀表达式。
中缀转前缀:
运用:
public class Test {
public static void main(String[] args) {
Stackstack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack);
System.out.println(stack.peek());//查看栈顶元素且不删除
System.out.println(stack.pop());//删除栈顶元素并返回栈顶元素的值
System.out.println(stack.peek());
stack.pop();
stack.pop();
stack.pop();
System.out.println(stack.empty());//判断栈元素是否为空,是则返回true
System.out.println(stack.isEmpty());//与上面那个方法一样
System.out.println(stack);
}
}
结果:
根据逆波兰表达式,求表达式的值。
有效的算符包括
+
、-
、*
、/
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
还记得我们先说的后缀表达式吗,后缀表达式其实就是逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
解题思路:
不是专门写讲解题目的,因此方法不是很巧妙,重点是明白栈的基本用法。
这道题其实不是特别难,首先分析,这道题给的是一个字符串数组,而每一个字符串不是数字就是运算符。因此我们可以设计一个函数来判断是否是运算符,如果是就返回true,不是就返回false:
private boolean isOperation(String s){ if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")){ return true; } return false; }
接着我们要明白,我们原来后缀表达式是把运算符放到了一个运算式的后面即把 1+1变为了1 1 + ,这就代表每两个数才会进行一次计算。因此我们可以设立一个下标 i 指向字符串数组for循环遍历,判断该字符串是否是数字,如果是数字,压入栈中,不是则从栈中弹出两个元素进行运算。这时我们就要注意一个问题,先出栈的是放在运算符的左边还是右边,我们可以想象一下,栈是后进先出,因此放右边的肯定是先出来的那个。然后将运算的结果再压入栈中。最后i到达边界了,那么栈中的数字就是运算结果。
代码实现:
class Solution {
public int evalRPN(String[] tokens) {
Stack stack = new Stack<>();
for (int i = 0; i < tokens.length; i++) {
String val = tokens[i];
if(!isOperation(val)){
stack.push(Integer.parseInt(val));
}else{
int num2 = stack.pop();//先弹出来的在右边
int num1 = stack.pop();
switch (val){
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
}
}
}
return stack.pop();
}
private boolean isOperation(String s){
if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")){
return true;
}
return false;
}
}
力扣OJ链接:逆波兰表达式。
给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。
这道题与我们最开始不可能的出栈顺序有点类似。即pushed是入栈顺序,poped是出栈顺序,如果两者不匹配,则返回false,匹配则返回true。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
解题思路:
首先我们分析之前做不可能的出栈顺序是怎们想的,因为在元素入栈后就可以出栈也可以继续入栈新元素。因此我们可以设立一个下标 j 指向出栈数组。新建一个栈,当入栈的元素不等于出栈的元素时,入栈该元素,如果相等,则下标 j ++,将该元素出栈。最后如果栈为空就返回true,否则返回false。
代码实现:
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stackstack = new Stack<>();
int j = 0;
for (int i = 0; i < pushed.length; i++) {
stack.push(pushed[i]);
while(j < popped.length && !stack.isEmpty() && stack.peek() == popped[j]){
stack.pop();
j++;
}
}
return stack.isEmpty();
}
}
力扣OJ链接:验证栈序列
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
解题思路:
遍历数组,如果是左括号则在栈中存入一个右括号,如果不是左括号则出栈一个元素,与这个相匹配,如果不相等则返回false,如果最后栈为空则返回true。
代码实现:
class Solution {
public boolean isValid(String s) {
Stack stack = new Stack<>();
for(char c : s.toCharArray()){
if(c == '(')stack.push(')');
else if(c == '{')stack.push('}');
else if(c == '[')stack.push(']');
else if(stack.isEmpty() || c !=stack.pop())return false;
}
return stack.isEmpty();
}
}
力扣OJ题链接:有效的括号
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
这个题相比一个栈,就多了一个一个方法获取最小值。
解题思路:
我们可以设计两个栈,一个栈用于正常的入栈出栈,一个栈用来存储最小值。
一开始将第一个元素都存入两个栈中,而后,之后的元素普通栈对应入栈出栈,而最小栈则在只有元素小于或等于最小栈的栈顶元素时才入栈。出栈时,只有出栈元素与最小栈的栈顶元素相等时才出栈。
代码实现:
class MinStack {
Stackstack ;
StackminStake;
public MinStack() {
stack = new Stack<>();
minStake = new Stack<>();
}
public void push(int val) {
if(!minStake.isEmpty() && minStake.peek() >= val){
minStake.push(val);
}else if(minStake.isEmpty()){
minStake.push(val);
}
stack.push(val);
}
public void pop() {
int sq = stack.pop();
if(!minStake.isEmpty() && sq == minStake.peek()){
minStake.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStake.peek();
}
}
力扣OJ链接:最小栈
前面的题和讲解我们也逐渐熟悉了栈,那么来模拟实现一下栈吧,简单模拟一下。
public class myStack {
public E []elem;
public static int usedSize = 0;
public myStack(){
this.elem =(E[]) new Object[5];
}
public void push(E e){
if(isFull()){
this.elem = Arrays.copyOf(elem,elem.length * 2);//2倍扩容
}
this.elem[usedSize] = e;
usedSize++;
}
public boolean isEmpty(){
return usedSize == 0;
}
public boolean isFull(){
return usedSize == elem.length;
}
public E pop(){
if(isEmpty()){
return null;
}
if(usedSize > 0) {
E e = this.elem[usedSize-1];
usedSize--;
return e;
}
return null;
}
public E peek(){
return this.elem[usedSize-1];
}
public String toString() {//重写toString方法
StringBuilder stringBuilder = new StringBuilder("[");
for (int i = 0; i < usedSize - 1; i++) {
stringBuilder.append(elem[i]);
stringBuilder.append(",");
}
stringBuilder.append(elem[usedSize-1]);
stringBuilder.append("]");
return stringBuilder.toString();
}
}
测试:
public static void main(String[] args) {
myStackmyStack = new myStack<>();
myStack.push(1);
myStack.push(2);
myStack.push(3);
myStack.push(4);
myStack.push(5);
myStack.push(6);
System.out.println(myStack);
System.out.println(myStack.peek());
System.out.println(myStack.pop());
System.out.println(myStack.pop());
System.out.println(myStack.pop());
System.out.println(myStack.pop());
}
结果:
栈的介绍到此告一段落。
本文收录专栏《数据结构》。