那么本篇,我们将介绍栈
栈其实是一种数据结构,用这么一个词可以来概括它:先进后出,后进先出
我们可以理解为:吃进去又吐出来
栈(Stack)是一种数据结构,它是一种线性数据结构,具有"先进后出"(Last In First Out, LIFO)的特性。栈可以看作是一种特殊的列表,只能在表的一端进行插入和删除操作,该一端称为栈顶。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
方法 | 功能 |
---|---|
Stack() | 构造一个空的栈 |
E push (E e) | 将e入栈,并返回e |
E pop() | 将栈顶元素出栈并返回 |
E peek() | 获取栈顶元素 |
int size() | 获取栈中的有效元素个数 |
boolean empty() | 检测栈是否为空 |
import java.util.Arrays;
public class MyStack {
private int[] elem;
private int usedSize;
private static final int DEFAULT_CAPACITY = 10;
public MyStack(){
this.elem=new int[DEFAULT_CAPACITY];
}
//判断是否为满
private boolean isFull(){
return usedSize == elem.length;
}
//判空
private boolean isEmpty(){
return usedSize == 0;
}
//入栈
public void push(int val){
if (isFull()){
System.out.println("栈已经满了,先进行扩容");
elem = Arrays.copyOf(elem,2*elem.length);
}
this.elem[usedSize++] = val;
}
//出栈
public int pop(){
if (isEmpty()){
throw new EmptyException();
}
int val = this.elem[usedSize-1];
usedSize--;
return val;
}
//获取栈顶元素
public int peek(){
if (isEmpty()){
throw new EmptyException();
}
return this.elem[usedSize-1];
}
//栈的大小
public int size(){
return usedSize;
}
//清空
public void clear(){
for (int i = 0; i <= usedSize; i++) {
elem[i] = 0;
}
usedSize = 0;
}
}
浏览器的前进和后退功能可以使用两个栈来实现。其中,一个栈保存已访问的页面,当用户点击后退按钮时,将当前页面压入另一个栈中;当用户点击前进按钮时,将已经后退的页面从另一个栈中弹出并压入第一个栈中。
栈可用于实现函数调用的调用栈。当一个函数调用另一个函数时,当前函数的状态(包括局部变量和返回地址)将被保存在栈中,直到被调用的函数执行完毕后,栈将弹出该函数的状态,从而返回到调用函数。
在文本编辑器中,可以使用栈来实现撤销操作。每当用户进行一次操作时,如插入、删除或替换文本,当前文本的状态将被保存在栈中,当用户点击撤销按钮时,将弹出最近的状态,从而恢复到之前的文本状态。
栈可以用于检查括号是否匹配。遍历字符串,当遇到左括号时,将其推入栈中,当遇到右括号时,与栈顶元素进行匹配。如果匹配成功,则将栈顶元素弹出,否则括号不匹配。如果最终栈为空,则表示所有括号都匹配。
有效的括号
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.empty()){
return false;
}
char top = stack.peek();
//此时top是左括号,ch是右括号
if(ch == ')' && top == '(' || ch =='}' && top == '{' || ch == ']' && top =='['){
stack.pop();
}else{
return false;
}
}
}
return stack.empty();
}
}
只要是左括号就入栈,只要是右括号,就开始匹配
栈常用于处理表达式求值。例如,当计算中缀表达式时,我们可以使用两个栈,一个用于操作符,一个用于操作数,通过遍历表达式并根据运算规则进行操作,最终得到表达式的结果。
150. 逆波兰表达式求值 - 力扣
在此之前,我们必须知道什么叫做逆波兰表达式~
这里也不得不提一嘴关于计算机里加减乘除的栈的相关操作了
首先,我们可以将待计算的表达式从左到右逐个字符地读取。遇到数字时,我们将其转换为相应的数值,并将其压入栈中。例如,表达式"23+4",我们先将数字2和3分别转换成数值2和3,然后将它们依次压入栈中。
当遇到运算符号(如加号、减号、乘号或除号)时,我们需要从栈中连续弹出两个操作数进行运算,并将运算结果压入栈中。取出的第一个弹出的操作数是运算符号右侧的操作数,而第二个弹出的操作数是运算符号左侧的操作数。
例如,对于表达式"23+4",当遇到加号时,我们需要从栈中弹出操作数2和3,然后执行2+3的计算,将计算结果5压入栈中。
对于减号、乘号和除号,也是类似的操作。例如,减法运算时,从栈中弹出操作数2和3,计算2-3,并将结果(-1)压入栈中。
在处理完所有运算符之后,栈中最后剩下的元素即为表达式的最终计算结果。
总结起来,栈在计算器中的加减乘除运算过程中的作用主要是用来存储待计算的操作数,并在遇到运算符时执行相应的计算操作。通过栈的后进先出的特性,可以很方便地实现计算器中的运算功能。
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(String s : tokens){
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();
}
private boolean isOperation(String s){
if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")){
return true;
}
return false;
}
}
栈的压入、弹出序列_牛客网
思路:
import java.util.*;
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]);
while(!stack.empty() && j < popV.length && stack.peek() == popV[j]){
stack.pop();
j++;
}
}
return stack.empty();
}
}
155. 最小栈 - 力扣
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if(minStack.empty()){
minStack.push(val);
}else{
if(val <= minStack.peek()){
minStack.push(val);
}
}
}
public void pop() {
if(!stack.empty()){
int ret = stack.pop();
if(minStack.peek() == ret){
minStack.pop();
}
}
}
//获取正常栈顶元素
public int top() {
if(stack.empty()){
return -1;
}
return stack.peek();
}
//获取最小栈
public int getMin() {
if(minStack.empty()){
return -1;
}
return minStack.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
至此,关于栈的介绍就到此为止,接下来会持续输出文章,敬请期待~