目录
前言:
这节中小编带你了解数据结构中的栈结构和队列结构,以及分别自己模拟实现它们,了解他们的使用方法和应用场景。
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<Integer> stack = new Stack<>();
//压栈操作:push
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
//出栈操作:pop
System.out.println(stack.pop());//4
System.out.println(stack.pop());//3
//查看栈顶元素操作:peek
System.out.println(stack.peek());//2
System.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());//4
System.out.println(myStack.pop());//3
//查看栈顶元素:peek()
System.out.println(myStack.peek());//2
System.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<Character> 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<Integer> 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<Integer> 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);
}
}
结果如下所示:
结束语:
这节博客中小编主要是和大家分享了如何去使用栈和栈的一些练习题,接下来大家一定要多多练习栈相关的习题,小编也会在后期出一些有关栈的一些习题的,大家敬请期待吧!大家继续跟紧小编的步伐一起往下走吧!!!希望对大家有所帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)