栈(数据结构系列7)
创始人
2025-05-30 15:35:17
0

前言:

这节中小编带你了解数据结构中的栈结构和队列结构,以及分别自己模拟实现它们,了解他们的使用方法和应用场景。

1.栈

1.1栈的概念

栈是一种特殊的线性结构,其只允许在固定的一端进行插入和删除元素操作,进行数据插入和删除操作的一端称之为栈顶,另一端称为栈底,栈中数据元素遵守后进先出的原则。(LIFO原则)

栈的形式和入栈、出栈如下图所示:

1.2栈的方法

我们可以直接在idea中看到Stack的一些方法,如下所示:

栈的使用方法
方法功能
Stack()构造一个空的栈
E push(E e)将e入栈,并返回e
E pop()将栈顶元素出栈并返回
E peek()获取栈顶元素
int size()获取栈中有效元素的个数
boolean empty()检测栈是否为空

我们可以通过创建一个栈来直接使用一下这些方法,具体代码如下所示:

package 栈的使用;import java.util.Stack;public class Test1 {public static void main(String[] args) {//创建出一个栈Stack stack = new Stack<>();//压栈操作:pushstack.push(1);stack.push(2);stack.push(3);stack.push(4);//出栈操作:popSystem.out.println(stack.pop());//4System.out.println(stack.pop());//3//查看栈顶元素操作:peekSystem.out.println(stack.peek());//2System.out.println(stack.peek());//2//获取栈中有效元素的个数:size()System.out.println(stack.size());//2//检测是否为空:isEmpty(),此处的isEmpty是继承自Vector的System.out.println(stack.isEmpty());//false}
}

结果如下所示:

1.3栈的模拟实现

下面我们来自己模拟实现一下栈的这些方法吧!

模拟栈代码如下所示:

package 栈的模拟实现;import java.util.Arrays;//通过数组来模拟实现
public class MyStack {public int[] elem;public int usedSize;public MyStack() {this.elem = new int[10];}//入栈public void push(int val) {//1.判断栈是否为满if (isFull()) {//进行扩容elem = Arrays.copyOf(elem, 2 * elem.length);}//2.压栈elem[usedSize] = val;usedSize++;}//判断栈是否为满public boolean isFull() {return usedSize == elem.length;}//出栈public int pop() {//1.判断是否为空if (isEmpty()) {throw new EmptyException("栈为空!!!");}//2.出栈return elem[--usedSize];}//查看栈顶元素public int peek() {//1.判断是否为空if (isEmpty()) {throw new EmptyException("栈为空!!!");}//2.出栈return elem[usedSize - 1];}//栈中的有效数字public int size() {return usedSize;}//栈是否为空public boolean isEmpty() {return usedSize == 0;}
}
package 栈的模拟实现;public class EmptyException extends RuntimeException{public EmptyException() {}public EmptyException(String mag) {super(mag);}
}

测试代码如下所示:

package 栈的模拟实现;public class Test {public static void main(String[] args) {//创建一个栈MyStack myStack = new MyStack();//入栈:push()myStack.push(1);myStack.push(2);myStack.push(3);myStack.push(4);//出栈:pop()System.out.println(myStack.pop());//4System.out.println(myStack.pop());//3//查看栈顶元素:peek()System.out.println(myStack.peek());//2System.out.println(myStack.peek());//2//获取栈中有效元素的个数:size()System.out.println(myStack.size());//2//检测是否为空:isEmpty()System.out.println(myStack.isEmpty());//false}
}


结果如下所示:

1.4练习题

1.括号匹配问题。

给出一个仅包含字符'(',')','{','}','['和']',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()"和"()[]{}"都是合法的括号序列,但"(]"和"([)]"不合法。

思想:
1.我们是借助栈来完成的。

2.首先遍历数组,然后再将是左括号的入栈。

3.如果获取到的是右括号,那么就从栈中查看栈顶元素,看是否和右括号匹配,如果匹配就直接出栈,如果不匹配就直接返回false。

4.当发现没有可以入栈的元素的时候就去查看此时的栈是否为空,如果为空那么就说明是匹配成功的,如果不为空那说明就是不匹配的,直接返回false。

代码如下所示:

package 栈的相关练习.括号匹配;import java.util.Scanner;
import java.util.Stack;public class Test1 {public static boolean isValid (String s) {// write code here//1.申请一个栈空间Stack stack = new Stack<>();//2.如果是左括号就入栈for (int i = 0; i < s.length(); i++) {char chl = s.charAt(i);if (chl == '{' || chl == '[' || chl == '(') {stack.push(chl);}else  {//遍历到了右括号,如果栈为空的话那么就说明都是右括号if (stack.isEmpty()) {return false;}//如果栈不为空,遇到了右括号就出栈看是否与之匹配char chr = stack.peek();//拿到左括号if (chl == '}' && chr == '{' || chl == ']' && chr == '[' || chl == ')' && chr == '(') {stack.pop();//将合理的直接弹出栈}else {return false;}}}if (!stack.isEmpty()) {return false;}return true;}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);String i = scanner.next();boolean ret = isValid(i);System.out.println(ret);}
}

结果如下所示:

 

 2.逆波兰表达式求值问题

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+'、'-'、'*' 和 '/' 。
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断 。
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

思想:

1.首先我们先来解释一下什么是逆波兰表达式。

如果知道的同学可以直接跳过哦,在我们从小接触的数学表达式中我们知道 1 + (2 + 3)* 4 - 5 所表达的含义,以及知道该如何去运算,但是其实还有几种方式可以表达数学式子,有前缀表达式、中缀表达式和后缀表达式,所谓前、中、后都是根据算数运算符的位置来定的,那么对于后缀表达式而言就是算术运算符在数字之后的表达式就叫后缀表达式啦,那么咱们现在所说的这个逆波兰表达式其实就是我现在所说的后缀表达式。

2.那么我们该怎么将一个式子转换成后缀表达式呢?

我们来看下面的这个式子:

1 + (2 + 3)* 4 - 5

我们给这个式子按照运算顺序给他加上括号,就变成下面的这种了:

 然后下一步就是按照每种括号的颜色,将对应的运算符拖到对应颜色的括号外面来:

最后就是删除这些没用的括号,就得到所谓的后缀表达式了也就是逆波兰表达式:

 3.解释了什么是逆波兰表达式之后,那么我们就可以借助我们这节所学的栈来完成运算了。

首先我们先遍历这个表达式,如果遇到数字就压入栈中,如下所示:

如果遇到运算符我们就弹出栈中的两个元素,第一个作为右操作数,第二个作为左操作数,(切记!!!这里的左右操作数不可以放反 )

然后再将所得结果放入栈中:

然后继续重复上述操作,直到栈为空为止,下面我们就来用代码来具体实现一下。 

代码如下所示:

class Solution {public int evalRPN(String[] tokens) {Stack stack = new Stack<>();//1.遍历数组for (int i = 0; i < tokens.length; i++) {String ch = tokens[i];//2.判断是否是运算符if (!isOperation(ch)){//不是运算符就是数字,则将数字入栈stack.push(Integer.parseInt(ch));//需要进行转换}else {//是运算符,弹出两个操作数,,进行运算,然后将运算的结果放入到栈中int left = stack.pop();//左操作数int right = stack.pop();//右操作数int count = 0;switch (ch) {case "+":count = right + left;break;case "-":count = right - left;break;case "*":count = right * left;break;case "/":count= right / left;break;}stack.push(count);}}return stack.pop();}private boolean isOperation(String x) {if (x.equals("+") || x.equals("-") || x.equals("*") || x.equals("/")) {return true;}return false;}
}

3.栈的压入弹出序列问题

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

思路:

1.我们来借助栈来完成,将给入栈序列和出栈序列分别加上两个指针来进行记录。

2.入栈时,如一个元素i就向后走一步,然后将栈顶元素与出栈序列中j下标的值进行对比如果相同则出栈,同时j向后走一步,如果不相同的话就直接返回false。

下面我们来通过代码来具体实现一下吧!

代码如下所示:

package 出栈入栈次序匹配;import java.util.Stack;public class Test {public static boolean IsPopOrder(int [] pushA, int [] popA) {//1.申请一个栈,用来入pushA中的值Stack stack = new Stack<>();//2.分别对两个序列进行标记int i = 0;int j = 0;while (i < pushA.length) {//入栈stack.push(pushA[i]);//判断是否和j下标的值相同while (j < popA.length && !stack.isEmpty() && stack.peek().equals(popA[j])) {stack.pop();j++;}i++;}return stack.empty();//检查栈是否为空}public static void main(String[] args) {int[] array1 = {1,2,3,4};int[] array2 = {4,3,2,1};boolean ret = IsPopOrder(array1,array2);System.out.println(ret);}
}

结果如下所示:

结束语:

这节博客中小编主要是和大家分享了如何去使用栈和栈的一些练习题,接下来大家一定要多多练习栈相关的习题,小编也会在后期出一些有关栈的一些习题的,大家敬请期待吧!大家继续跟紧小编的步伐一起往下走吧!!!希望对大家有所帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)

相关内容

热门资讯

【十二天学java】day04... 第一章 流程控制语句 在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影...
Redis(十):主从模式 前言 上一篇介绍了 Redis 应对并发问题的方案。这节开始介绍 Redis 的主从模式。 由于 R...
C指针:程序员的望远镜 C指针:程序员的望远镜一、什么是指针1.1 指针的定义1.2 指针和普通变量的区别1....
牛客网Java面试题及答案整理... 学习如逆水行舟,尤其是 IT 行业有着日新月异的节奏,我们更要抓紧每一次...
江西宜春智慧停车欠费清缴享7折... 极目新闻记者 杜光然 近日,江西网友发视频称,宜春智慧停车公司开启了端午缴费特惠活动,车主享停车费7...
律数科技申请基于区块链的小额金... 金融界2025年5月31日消息,国家知识产权局信息显示,北京律数科技有限公司申请一项名为“一种基于区...
好用的5款国产低代码平台介绍 一、云程低代码平台 云程低代码平台是一款基于springboot、vue.js技术的企业级低代码...
【数据结构第三章】- 队列 目录 一、队列的定义和特点 二、循环队列 2.1 - CircularQueue.h 2.2 - C...
如何将pdf文件压缩?pdf压... PDF是一种常见的文档格式,因为包括文本格式和图像,我们往往采用这种格式...
0X30数学知识 - 质数 定义: 若一个正整数无法被除了1和它自身之外的任何自然数整除,则称该数...