1、什么是栈?
栈其实就是一种数据结构,特点:先进后出。
2、什么是java虚拟机栈?
此时 JVM stack 只是JVM当中的一块内存,该内存一般用来存放,例如:局部变量......
调用函数的时候,我们会为这个函数开辟一块内存,叫做栈帧。在哪里开辟呢?JVM stack
入栈和出栈顺序例题:
一个栈的入栈序列是 a,b,c,d,e,则栈的不可能的输出序列是( ) 。
a) edcba
b) decba
c) dceab
d) abcde
栈讲究先进后出,后进先出
选项1是abcde先入栈,然后依次出栈,正好是edcba
选项2是abcd先依次入栈,然后d出栈,e再入栈,e出栈
选项3是错误的,不可能a先出栈
选项4是a入栈,然后a出栈;b再入栈,b出栈。。。。。。依此类推
中缀表达式转后缀表达式(逆波兰表达式):
中缀表达式:(5+4)*3-2
过程:(((5+4)*3)-2) ---> ((54)+*3)-2) ---> (54+3)*-2) ---> 54+3*2-
后缀表达式:54+3*2-
如何通过后缀表达式计算一个值?(利用栈)
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +
、-
、*
、/
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < tokens.length; i++){
String s = tokens[i];
if (!isOperation(s)){
stack.push(Integer.parseInt(s));
}else{
int num2 = stack.pop();
int num1 = stack.pop();
switch(s){
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();
}
public boolean isOperation(String s){
if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")){
return true;
}
return false;
}
}
栈的方法:
public class TestDemo {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack);//[1, 2, 3, 4]
System.out.println(stack.pop());//弹出栈顶元素,并且删除4
System.out.println(stack.pop());//3
System.out.println(stack.peek());//获取栈顶元素,但不删除2
System.out.println(stack.empty());//false
System.out.println(stack.isEmpty());//false
}
}
3、自己实现一个栈
import java.util.Arrays;
public class MyStack {
private int[] elem;
private int usedSize;
public MyStack(int[] elem) {
this.elem = new int[5];
}
public void push(int val){
if (isFull()){
//扩容
elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize] = val;
usedSize++;
}
public boolean isFull(){
return usedSize == elem.length;
}
public boolean isEmpty(){
return usedSize == 0;
}
public int pop(){
if (isEmpty()) {
throw new RuntimeException();
}
int oldVal = elem[usedSize-1];
usedSize--;
return oldVal;
}
public int peek(){
if (isEmpty()) {
throw new RuntimeException();
}
int oldVal = elem[usedSize-1];
return oldVal;
}
}
此时我们自己实现的栈,底层是一个数组,那么请问:能不能用单链表实现一个栈?
可以。满足两个要求:1、先进后出;2、入栈和出栈的时间复杂度得是O(1)
因为链表可以头插也可以尾插,那么入栈使用头插法还是尾插法?
假设:头插法入栈,时间复杂度是O(1),出栈的时候,只需要删除头节点[出栈]就好,时间复杂度也是O(1)。
如果入栈是尾插法,那么时间复杂度是O(n)了,因为尾插法每次都要找尾巴。
如果非要尾插法呢?但是还是做不到,此时你入栈是O(1)了,出栈呢?因为是单链表,虽然最后一个节点,我知道是谁,但是他的前一个我就不知道了。还要遍历去找这个节点的前驱,时间复杂度又是O(n).
其实最终的解决办法就是使用双向链表来实现一个栈,这样完全是可以的。