标签:return int System Java public 表达式 out
栈的总结
栈的基本知识
栈的介绍
- 栈的英文为(stack)
- 栈是一个先入后出(FILO-FirstInLastOut)的有序列表。
- 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
- 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。
图解理解出栈(pop)和入栈(push)的概念:
栈的应用场景
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
- 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
- 表达式的转换[中缀表达式转后缀表达式]与求值
- 二叉树的遍历。
- 图形的深度优先(depth first search)搜索法。
栈的快速入门
使用数组模拟栈
图解:
代码实现:
package com.athome.stack;
/**
* Description:使用数组模拟栈
* Author:江洋大盗
* Date:2021/1/22 20:23
*/
public class ArrayStack {
private final int maxSize;//控制栈的大小
private final int[] array;//模拟栈的数组
private int top = -1;//将栈顶初始化为-1
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
array = new int[maxSize];
}
/**
* @return 若栈已满返回true,否则返回false
*/
public boolean isFull() {
return top == maxSize - 1;
}
/**
* @return 若栈为空返回false, 否则返回true
*/
public boolean isEmpty() {
return top == -1;
}
/**
* 将元素压入栈
*
* @param val 待压入栈的元素
*/
public void push(int val) {
//首先判断栈是否已满
if (isFull()) {
System.out.println("栈已满,不能添加");
return;
}
array[++top] = val;
}
/**
* 弹出栈顶元素
*
* @return 返回栈顶元素
*/
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈为空,不能弹出元素");
}
return array[top--];
}
/**
* 展示栈中的元素
*/
public void showStack() {
if (isEmpty()) {
throw new RuntimeException("栈为空,不能弹出元素");
}
for (int i = top; i >= 0; i--) {
System.out.println("array[" + i + "] = " + array[i]);
}
}
}
主方法中测试:
package com.athome.stack;
import java.util.Scanner;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/22 20:23
*/
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(5);
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("-------------------------");
System.out.println("show:展示数据");
System.out.println("push:添加数据");
System.out.println("pop:弹出数据");
System.out.println("exit:退出程序");
System.out.print("请输入您的操作:");
switch (scanner.next()) {
case "show":
try {
stack.showStack();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case "push":
System.out.print("请输入添加的数据:");
stack.push(scanner.nextInt());
break;
case "pop":
try {
System.out.println("取出数据:" + stack.pop());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case "exit":
loop = false;
}
}
System.out.println("程序结束");
}
}
使用链表模拟栈
首先创建节点类:
package com.athome.stack;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/22 20:40
*/
public class Hero {
private String name;//武将姓名
private int power;//武力值
private Hero next;//下一个节点
public Hero(String name, int power) {
this.name = name;
this.power = power;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPower() {
return power;
}
public void setPower(int power) {
this.power = power;
}
public Hero getNext() {
return next;
}
public void setNext(Hero next) {
this.next = next;
}
@Override
public String toString() {
return "武将{" +
"姓名:'" + name + '\'' +
", 武力值:" + power +
'}';
}
}
链表模拟栈的实现:
package com.athome.stack;
/**
* Description:使用链表模拟栈
* Author:江洋大盗
* Date:2021/1/22 20:43
*/
public class LinkedListStack {
private final int maxSize;
private final Hero head = new Hero(null, 0);
private int count = 0;//辅助计数器
public LinkedListStack(int maxSize) {
this.maxSize = maxSize;
}
/**
* @return 如果栈已满返回true,否则返回false
*/
public boolean isFull() {
return count == maxSize;
}
/**
* @return 如果栈为空返回true,否则返回false
*/
public boolean isEmpty() {
return count == 0;
}
/**
* @return 返回当前栈中的元素个数
*/
public int size() {
if (isEmpty()) {
return 0;
}
if (isFull()) {
return maxSize;
}
Hero temp = head.getNext();
int size = 0;
while (temp != null) {
size++;
temp = temp.getNext();
}
return size;
}
/**
* 压入数据
*
* @param hero 待压入的节点
*/
public void push(Hero hero) {
if (isFull()) {
System.out.println("栈已满");
return;
}
Hero temp = head;
for (int i = 0; i < count; i++) {
temp = temp.getNext();
}
temp.setNext(hero);
count++;
}
/**
* @return 弹出栈顶元素
*/
public Hero pop() {
if (isEmpty()) {
throw new RuntimeException("栈为空");
}
Hero temp = head;
for (int i = 0; i < count; i++) {
temp = temp.getNext();
}
count--;
return temp;
}
/**
* 输出栈中的元素
*/
public void list() {
if (isEmpty()) {
throw new RuntimeException("栈为空");
}
Hero temp = head;
for (int i = 0; i < count; i++) {
for (int j = 0; j < count - i; j++) {
temp = temp.getNext();
}
System.out.println(temp);
temp = head;
}
}
}
主方法中测试:
package com.athome.stack;
import java.util.Scanner;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/22 21:07
*/
public class LinkedListStackDemo {
public static void main(String[] args) {
LinkedListStack linkedListStack = new LinkedListStack(3);
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("-------------------------");
System.out.println("show:展示数据");
System.out.println("push:添加数据");
System.out.println("pop:弹出数据");
System.out.println("size:求出当前栈的大小");
System.out.println("exit:退出程序");
System.out.print("请输入您的操作:");
switch (scanner.next()) {
case "show":
try {
linkedListStack.list();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case "push":
System.out.print("请输入武将姓名:");
String name = scanner.next();
System.out.print("请输入武将武力值:");
int power = scanner.nextInt();
linkedListStack.push(new Hero(name, power));
break;
case "pop":
try {
System.out.println("取出数据:" + linkedListStack.pop());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case "size":
int size = linkedListStack.size();
System.out.println(size);
break;
case "exit":
loop = false;
}
}
System.out.println("程序结束");
}
}
栈的实际应用
前缀,中缀和后缀表达式的基本知识
前缀表达式
前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3)。
求值方法:对前缀表达式求值,要从右至左扫描表达式,首先从右边第一个字符开始判断,若当前字符是数字则一直到数字串的末尾再记录下来,若为运算符,则将右边离得最近的两个“数字串”作相应运算,然后以此作为一个新的“数字串”并记录下来;扫描到表达式最左端时扫描结束,最后运算的值即为表达式的值。
例如:对前缀表达式“- 1 + 2 3”求值,扫描到3时,记录下这个数字串,扫描到2时,记录下这个数字串,当扫描到+时,将+右移做相邻两数字串的运算符,记为2+3,结果为5,记录下5这个新数字串,然后继续向左扫描,扫描到1时,记录下这个数字串,扫描到-时,将-右移做相邻两数字串的运算符,记为1-5,结果为-4,此时关于这个表达式的全部运算已完成,故表达式的值为-4。
中缀表达式
中缀表达式是一个通用的算术或逻辑公式表示方法。 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。
与前缀表达式(例:+ 3 4)或后缀表达式(例:3 4 +)相比,中缀表达式不容易被计算机解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。
后缀表达式
逆波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)。
中缀表达式的计算
思路分析:
代码实现:
首先利用数组模拟一个栈
package com.athome.calculator;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/22 21:28
*/
public class ArrayStack {
private final int maxSize;
private int top = -1;//初始化为-1
private final int[] arr;
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
arr = new int[maxSize];
}
/**
* @return 若栈已满返回true,否则返回false
*/
public boolean isFull() {
return top == maxSize - 1;
}
/**
* @return 若栈为空返回false, 否则返回true
*/
public boolean isEmpty() {
return top == -1;
}
/**
* 将元素压入栈
*
* @param value 待压入栈的元素
*/
public void push(int value) {
//判断栈是否已满
if (isFull()) {
System.out.println("栈已满");
return;
}
arr[++top] = value;
}
/**
* 弹出栈顶元素
*
* @return 返回栈顶元素
*/
public int pop() {
//判断栈是否为空
if (isEmpty()) {
throw new RuntimeException("栈为空");
}
return arr[top--];
}
/**
* 展示栈中的元素
*/
public void showStack() {
//判断栈是否为空
if (isEmpty()) {
throw new RuntimeException("栈为空");
}
for (int i = top; i >= 0; i--) {
System.out.println("arr[" + i + "] = " + arr[i]);
}
}
/**
* @param operator 传入的字符
* @return 如果该字符是运算符返回true,否则返回false
*/
public boolean isOperator(char operator) {
return operator == '+' || operator == '-' || operator == '*' || operator == '/';
}
/**
* @param operator 运算发
* @return 返回该运算符的级别
*/
public int priority(int operator) {
if (operator == '-' || operator == '+') {
return 0;
} else if (operator == '*' || operator == '/') {
return 1;
} else {
return -1;
}
}
/**
* @param num1 数字1
* @param num2 数字2
* @param operator 运算符
* @return 返回运算结果
*/
public int arithmetic(int num1, int num2, int operator) {
if (operator == '+') {
return num1 + num2;
} else if (operator == '-') {
return num2 - num1;
} else if (operator == '*') {
return num1 * num2;
} else if (operator == '/') {
return num2 / num1;
} else {
throw new RuntimeException("参数有误");
}
}
/**
* 查看栈顶元素,不会将该元素从栈中弹出
*
* @return 返回栈顶元素
*/
public int peek() {
if (isEmpty()) {
throw new RuntimeException("栈为空");
}
return arr[top];
}
}
计算中缀表达式:
package com.athome.calculator;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/22 21:26
*/
public class InfixCalculator {
/**
* @param expression 中缀表达式
* @return 返回运算结果
*/
public int calculate(String expression) {
//创建数字栈,存放数字
ArrayStack numStack = new ArrayStack(10);
//创建符号栈,存放符号
ArrayStack opeStack = new ArrayStack(10);
int num1;//接受栈顶元素
int num2;//接受栈顶元素的下一个元素
char waitOpe;//记录扫描到的待操作字符
int res;//接受运算结果
int operator;//记录运算符(注:java底层char与int是可以互相表示的)
StringBuilder addKey = new StringBuilder();//用于拼接多位数
int index = 0;//用于扫描
do {
waitOpe = expression.charAt(index);
//首先判断接受到的字符是否为一个运算符
if (numStack.isOperator(waitOpe)) {
//如果是运算符,首先判断符号栈是否为空
//如果为空直接将该字符加入符号栈
if (!opeStack.isEmpty()) {
//不为空,判断当前符号与符号栈栈顶符号的运算优先级
//如果当前符号优先级大,直接加入符号栈
if (opeStack.priority(waitOpe) <= opeStack.priority(opeStack.peek())) {
//当前符号运算优先级小,需要将数字栈栈顶前两个元素弹出,与符号栈栈顶元素进行运算。
//再将结果加入数字栈,做完这个工作后,当前符号加入符号栈
num1 = numStack.pop();
num2 = numStack.pop();
operator = opeStack.pop();
res = numStack.arithmetic(num1, num2, operator);
numStack.push(res);
}
}
//最终都得将该符号加入符号栈
opeStack.push(waitOpe);
} else {
//如果该字符为一个数字,需要判断其是否为一个多位数
addKey.append(waitOpe);
//如果index已经再最后一位了,就无需判断是否为多位数
if (index == expression.length() - 1) {
numStack.push(Integer.parseInt(new String(addKey)));
} else {
//如果index的下一位是符号说明多位数的判断可以结束
if (opeStack.isOperator(expression.charAt(index + 1))) {
numStack.push(Integer.parseInt(new String(addKey)));
//切记,将addKey清空
addKey.delete(0, addKey.length());
}
}
}
//切记
index++;
} while (index < expression.length());
//只要符号栈不为空,就一直进行运算,直到符号栈为空
while (!opeStack.isEmpty()) {
num1 = numStack.pop();
num2 = numStack.pop();
operator = opeStack.pop();
res = numStack.arithmetic(num1, num2, operator);
numStack.push(res);
}
return numStack.pop();
}
public static void main(String[] args) {
String expression = "30/3+5*2-6";//14
//测试中序表达式的计算方法是否正确
InfixCalculator calculator = new InfixCalculator();
int calculate = calculator.calculate(expression);
System.out.println("结果为:" + calculate);
}
}
逆波兰计算器的实现
观察上面的代码可以知道,计算机计算中缀表达式是比较困难的。我们分析后缀表达式的特点可以知道,计算机计算后缀表达式比较容易,所以我们需要想办法将中缀表达式转换为后缀表达式。
中缀转后缀的算法实现步骤
首先需要分配2个栈,一个作为临时存储运算符的栈S1,一个作为存放结果(逆波兰式)的栈S2(空栈),逐序进行如下步骤:
- 若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈。
- 若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符(不包括括号运算符)优先级高于S1栈栈顶运算符(包括左括号)优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符(包括左括号)低于(不包括等于)该运算符优先级时停止弹出运算符,最后将该运算符送入S1栈。
- 若取出的字符是“(”,则直接送入S1栈顶。
- 若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
- 重复上面的1~4步,直至处理完所有的输入字符。
代码实现:
这个类是比较符号运算优先级的工具类
package com.athome.calculator;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/22 22:25
*/
public class Operate {
private static final int ADD = 1;
private static final int SUB = 1;
private static final int MUL = 2;
private static final int DIV = 2;
/**
* @param operation 传入的符号
* @return 返回符号的运算优先级
*/
public static int getValue(String operation) {
int res = 0;
switch (operation) {
case "+":
res = ADD;
break;
case "-":
res = SUB;
break;
case "*":
res = MUL;
break;
case "/":
res = DIV;
break;
case "(":
break;
default:
throw new RuntimeException("运算符有误");
}
return res;
}
}
核心代码实现:
package com.athome.calculator;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/22 22:05
*/
public class PolandNation {
/**
* 获取中缀表达式的方法
*
* @param infixExpression 中缀表达式
* @return 返回封装中缀表达式的集合
*/
public List<String> getInfix(String infixExpression) {
ArrayList<String> list = new ArrayList<>();
int index = 0;//遍历中缀表达式的指针
StringBuilder addKey = new StringBuilder();//用作多位数的拼接
char c;//接收从中缀表达式上截取的字符
while (index < infixExpression.length()) {
//如果不是数字,即该字符为符号,直接加入集合
if ((c = infixExpression.charAt(index)) < 48 ||
(c = infixExpression.charAt(index)) > 57) {
list.add(String.valueOf(c));
index++;
} else {
//清空addKey
addKey.delete(0, addKey.length());
while (index < infixExpression.length() &&
(c = infixExpression.charAt(index)) >= 48
&& (c = infixExpression.charAt(index)) <= 57) {
addKey.append(c);
index++;
}
list.add(String.valueOf(addKey));
}
}
return list;
}
/**
* 中缀转后缀的方法
*
* @param infixList 封装中缀表达式的集合
* @return 返回封装后缀表达式的集合
*/
public List<String> infixToSuffix(List<String> infixList) {
Stack<String> opeStack = new Stack<>();//符号栈
ArrayList<String> list = new ArrayList<>();
//开始转换
for (String s : infixList) {
if (s.matches("\\d+")) {
//如果s是数字,直接装入集合中
list.add(s);
} else if ("(".equals(s)) {
//如果s是左括号,直接入符号栈
opeStack.push(s);
} else if (")".equals(s)) {
如果s是),就从符号栈中依次弹出符号存放到集合中,直到遇到(为止
while (opeStack.size() != 0 && !"(".equals(opeStack.peek())) {
list.add(opeStack.pop());
}
//切记消除括号
opeStack.pop();
} else {
//其他符号需要比较优先级,如果栈顶优先级大于等于该符号优先级,则将栈顶元素依次添加到集合中。
//直到栈顶元素的优先级小于该符号优先级
while (opeStack.size() != 0 &&
Operate.getValue(opeStack.peek()) >= Operate.getValue(s)) {
list.add(opeStack.pop());
}
//切记将该符号加入栈中
opeStack.push(s);
}
}
//将符号栈中符号依次添加到集合中
while (opeStack.size() != 0) {
list.add(opeStack.pop());
}
return list;
}
/**
* 运算的方法
*
* @param suffixList 封装后缀表达式的集合
* @return 输出运算结果
*/
public int calculate(List<String> suffixList) {
Stack<String> numStack = new Stack<>();//数字栈
int num1;//记录栈顶元素的下一个元素
int num2;//记录栈顶元素
int res = 0;//记录结果
for (String s : suffixList) {
if (s.matches("\\d+")) {
//如果s是数字,直接入栈
numStack.push(s);
} else {
num2 = Integer.parseInt(numStack.pop());
num1 = Integer.parseInt(numStack.pop());
switch (s) {
case "+":
res = num1 + num2;
break;
case "-":
res = num1 - num2;
break;
case "*":
res = num1 * num2;
break;
case "/":
res = num1 / num2;
break;
}
numStack.push(String.valueOf(res));
}
}
return Integer.parseInt(numStack.pop());
}
public static void main(String[] args) {
PolandNation polandNation = new PolandNation();
String infixExpression = "32+78/(20+6)";
//测试getInfix方法
List<String> infixList = polandNation.getInfix(infixExpression);
System.out.println(infixList);
//测试infixToSuffix方法
List<String> suffix = polandNation.infixToSuffix(infixList);
System.out.println(suffix);
//测试calculate方法
int res = polandNation.calculate(suffix);
System.out.println("运算结果:" + res);
}
}
结语
只要能收获甜蜜,荆棘丛中也有蜜蜂忙碌的身影,未来的你一定会感谢现在努力的自己。
标签:return,int,System,Java,public,表达式,out 来源: https://blog.csdn.net/m0_52099927/article/details/113007316
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。