目录
一、栈(Stack)
1.1 概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则(也就是先进后出)
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈。出数据在栈顶
1.2 栈的使用
方法 | 功能 |
Stack() | 构造一个空的栈 |
E push(E e) | 将e入栈,并返回e |
E pop() | 将栈顶元素出栈并返回(有返回值) |
E peek() | 获取栈顶元素 |
int size() | 获取栈中有效元素个数 |
boolean empty() | 检测栈是否为空 |
public static void main(String[] args) {
Stack<Integer> s = new Stack<>();
s.push(1);
s.push(2);
s.push(3);
s.push(4);
System.out.println(s.size()); // 获取栈中有效元素个数---> 4
System.out.println(s.peek()); // 获取栈顶元素---> 4
s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
if (s.empty()) {
System.out.println("栈空");
} else {
System.out.println(s.size());
}
}
1.3 栈的模拟实现
从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的
栈的模拟实现有两种:一种是数组实现,另一种是链表(单链表或者双链表)实现,不管是哪种,都得保证入栈 出栈操作的时间复杂度为O(1)
下面这个是数组模拟实现栈的方式:
import java.util.Arrays;
//数组实现栈
public class MyStack {
public int[] elem;//定义数组
public int uesdSize;//记录当前数组的有效元素的个数,同时可以当作下标使用
public static final int DEFAULT_CAPACITY = 10;//默认容量大小
public MyStack() {
this.elem = new int[DEFAULT_CAPACITY];
}
//判断栈是否满了
public boolean isFull() {
return uesdSize == elem.length;//这里不能写成DEFAULT_CAPACITY,DEFAULT_CAPACITY被final修饰不能变
}
//压栈 入栈
public void push(int val) {
if (isFull()) {
this.elem = Arrays.copyOf(elem,2*elem.length);//扩容为原数组
} else {
elem[uesdSize++] = val;
}
}
//判空
public boolean isEmpty() {
return uesdSize == 0;
}
//出栈
public int pop() {
if (isEmpty()) {
throw new EmptyStackException("栈为空...");
}
int oldVal = elem[uesdSize-1];
uesdSize--;
elem[uesdSize] = 0;
return oldVal;
}
//获取栈顶元素
public int peek() {
if (isEmpty()) {
throw new EmptyStackException("栈为空...");
}
return elem[uesdSize-1];
}
}
1.4 栈的应用场景
1. 改变元素的序列
2. 将递归转化为循环
比如:逆序打印链表
// 递归方式
void printList(Node head){
if(null != head){
printList(head.next);
System.out.print(head.val + " ");
}
}
这里循环的方式就类似上面的第二题,入栈元素出栈也就相当于逆序
// 循环方式
void printList(Node head){
if(null == head){
return;
}
Stack<Node> s = new Stack<>();
// 将链表中的结点保存在栈中
Node cur = head;
while(null != cur){
s.push(cur);
cur = cur.next;
}
// 将栈中的元素出栈
while(!s.empty()){
System.out.print(s.pop().val + " ");
}
}
3. 括号匹配
这题的思路:
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
//遍历字符串
for(int i=0;i<s.length();i++) {
char ch = s.charAt(i);
if(ch=='{'||ch=='['||ch == '(') {
//左括号入栈
stack.push(ch);
} else {
//右括号
if(stack.isEmpty()) {
//栈为空
return false;
}
//栈不为空,右括号判断匹配
char ch2 = stack.peek();
if(ch2=='{'&&ch=='}'||ch2=='['&&ch==']'||ch2=='('&&ch==')') {
stack.pop();
} else {
return false;
}
}
}
//遍历完了,但是栈不为空
if(!stack.isEmpty())
return false;
return true;
//return stcak.isEmpty() 可以直接代替前三行
}
}
4. 逆波兰表达式求值
看这题之前,我们先来学习一下什么是前中后缀表达式,中缀表达式 转 后缀表达式 ,最后再来看怎么根据后缀表达式计算结果
在这里代码题考的最多的就是根据后缀表达式计算结果,那么思路是什么呢?
代码实现:
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(int i=0;i<tokens.length;i++) {
String str = tokens[i];
if(!isOperatons(str)) {
//不是运算符,也就是数字
//将字符串转为数字
int val = Integer.valueOf(str);
//将数字入栈
stack.push(val);
} else {
//是运算符
//弹除两个栈顶元素,第一个为右操作数
int num2 = stack.pop();
int num1 = stack.pop();
//计算
switch(str) {
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 isOperatons(String str) {
if(str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")) {
return true;
} else {
return false;
}
}
}
5. 出栈入栈次序匹配
如下图 当pushV栈顶元素和popV[j]一样,我们是需要将pushA的栈顶元素出栈的,不然无法判断下一个元素是否相等
public class Solution {
public boolean IsPopOrder (int[] pushV, int[] popV) {
Stack<Integer> stack = new Stack();
int j =0;
for(int i=0;i<pushV.length;i++) {
stack.push(pushV[i]);
//如果pushV栈顶元素和popV[j]一样
while(!stack.isEmpty()&&j<popV.length&&stack.peek()==popV[j]) {
j++;
stack.pop();
}
}
if(j<popV.length) {
return false;
}
return true;
//return j == popV.length; //这里行可以代替前三行
//return stack.isEmpty; //或者这样写也行
}
}
6. 最小栈
class MinStack {
Stack<Integer> stack;
Stack<Integer> minStack;
//构造方法:初始化两个栈
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
//如果第一次放(也就是minStack为空),直接放即可
if(minStack.isEmpty()) {
minStack.push(val);
} else {
//不是第一次放,那就只有val<= minStack栈顶元素才可以放
if(val<= minStack.peek()) {
minStack.push(val);
}
}
}
public void pop() {
//根据题目不用考虑空栈
int val = stack.pop();
//如果普通栈pop出的元素就是最小,那么minStack也需要pop
if(minStack.peek()==val)
{
minStack.pop();
}
}
//获取栈顶元素
public int top() {
return stack.peek();
}
//获取最小值
public int getMin() {
return minStack.peek();
}
}
1.5 概念区分
栈、虚拟机栈、栈帧有什么区别呢?
本次内容就到此啦,欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 !