栈的概念
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素
- 一种受限的线性表:仅能在表尾插入(入栈)、删除(出栈)
- 表尾称为栈顶;另一端称为栈底
- 栈:先进后出
Java基于数组实现栈
基于数组的栈有三个成员变量:
- maxSize:表示栈大小(可以不用,内部设置)
- int[] stack:数组
- top:栈顶指针
实现栈需要有这些方法:
- isFull():
top == maxSize -1
判断栈满 - isEmpty():
top == -1
判断栈空 - push(int value):入栈,往栈顶加入元素
- pop():出栈,删除栈顶元素
- show():遍历栈
- peek():显示栈顶元素,不出栈
/**
* 表示栈结构
*/
class ArrayStack{
//栈的大小
private int maxSize;
//数组模拟栈
private int[] stack;
//top表示为栈顶,初始化为-1
private int top = -1;
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
this.stack = new int[maxSize];
}
/**
* @return
* 判断栈满
*/
public boolean isFull(){
return top == maxSize -1;
}
/**
* @return
* 判断栈空
*/
public boolean isEmpty(){
return top == -1;
}
/**
* @param value 入栈值
*
* 入栈
*/
public void push(int value){
//判断是否栈满
if (isFull()){
System.out.println("=== 栈满 ===");
return;
}
//指针后移
top++;
stack[top] = value;
}
/**
* @return
*
* 出栈
*/
public int pop(){
//判断是否栈空
if (isEmpty()){
throw new RuntimeException("=== 栈空,没有数据 ===");
}
int value = stack[top];
top--;
return value;
}
/**
* 遍历栈
*/
public void show(){
//判断是否栈空
if (isEmpty()){
System.out.println("=== 栈空,没有数据 ===");
return;
}
//从栈顶开始显示数据
for (int i = top;i >= 0;i--){
System.out.println("stack["+i+"] = "+stack[i]);
}
}
/**
* @return
* 返回当前栈顶的值,不出栈
*/
public int peek(){
return stack[top];
}
}
测试类:
public class ArrayStackDemo {
//测试
public static void main(String[] args) {
ArrayStack arrayStack = new ArrayStack(5);
String key = "";
boolean loop = true;
Scanner scanner = new Scanner(System.in);
while (loop){
System.out.println("show:显示栈");
System.out.println("exit:退出程序");
System.out.println("push:入栈");
System.out.println("pop:出栈");
System.out.println("请输入选择:");
key = scanner.next();
switch (key){
case "show":
arrayStack.show();
break;
case "push":
System.out.println("请输入入栈的数据:");
int value = scanner.nextInt();
arrayStack.push(value);
break;
case "pop":
try {
int popValue = arrayStack.pop();
System.out.println("出栈值:"+popValue);
}
catch (Exception e){
System.out.println(e.getMessage());
}
break;
case "exit":
scanner.close();
loop = false;
break;
default:
System.out.println("请输入正确的key");
break;
}
}
System.out.println("=== 程序退出 ===");
}
}
实现计算器
计算器都有paste功能
即粘贴一个字符串表达式:3+10*2-4/2-7
可以通过栈实现(仅实现了加减乘除,且没有错误表达式判断)
实现思路:
- 创建两个栈:数栈、符号栈;数栈存放表达式中的数字,符号栈存放运算符
- 设置一个扫描字符串索引index,一个字符一个字符的扫描表达式expression
- 当扫描为运算符时:
3.1. 当符号栈为空,直接压入运算符
3.2. 当符号栈不为空,判断栈中运算符与当前运算符优先级。当前运算符优先级小于或等于栈中运算符,拿出数栈的两个数字和栈中的栈顶运算符运算,然后将结果压入数栈,将当前运算符压入符号栈 - 当运算符扫描完,判断符号栈是否空,不为空(肯定只剩一个符号),弹出数栈两个数据符号栈运算符,计算得到结果压入数栈,该结果就是表达式运算结果
需要往栈结构ArrayStack里添加三个方法:判断字符是否为运算符、计算字符优先级、计算方法
/**
* @param oper 传入的运算符
* @return 返回优先级
*
* 返回运算符的优先级,优先级使用数字表示
* 数字越大优先级越高
*/
public int priority(int oper){
//乘除运算符优先级为1
if (oper == '*' || oper == '/'){
return 1;
}
//加减运算符优先级为0
else if (oper == '+' || oper == '-'){
return 0;
}
//假定目前表达式只有 + - * /
else {
return -1;
}
}
/**
* @param val 运算符字符
* @return
* 判断是不是一个运算符
*/
public boolean isOper(char val){
return val == '+' || val == '-' || val == '*' || val == '/';
}
/**
* @param num1 表达式后一个数
* @param num2 表达式前一个数
* @param oper 运算符
* @return
* 计算方式,得到运算结果
*/
public int cal(int num1,int num2,int oper){
int result = 0;
switch (oper){
case '+':
result = num1 + num2;
break;
case '-':
result = num2 - num1;
break;
case '*':
result = num1 * num2;
break;
case '/':
result = num2 / num1;
break;
default:
break;
}
return result;
}
计算器Calculator:根据前面的步骤完成
package com.company.stackCalculator;
import java.util.Scanner;
/**
* @author zfk
*
* 栈实现计算器
*/
public class Calculator {
public static void main(String[] args) {
System.out.println("请输入表达式:");
//表达式
Scanner scanner = new Scanner(System.in);
String expression = scanner.nextLine();
//创建两个栈:数栈、符号栈
ArrayStack numStack = new ArrayStack(5);
ArrayStack operStack = new ArrayStack(5);
//扫描表达式的索引
int index = 0;
//运算
int num1 = 0;
int num2 = 0;
int oper = 0;
int result = 0;
//将每次扫描到的char保存到ch
char ch = ' ';
//多位数时,用于拼接
String keepNum = "";
//开始扫描expression
while (true){
//依次得到expression的每一个字符
ch = expression.substring(index,index+1).charAt(0);
//判断ch是否为运算符
if (operStack.isOper(ch)){
//判断当前符号栈是否为空
if (!operStack.isEmpty()){
//不为空,处理:
//判断优先级,如果当前运算符的 优先级小于、等于 栈中的运算符,需要从数栈中pop出两个数
//再从符号栈中pop出运算符,运算得到结果,压入数栈,然后将当前运算符压入符号栈
if (operStack.priority(ch) <= operStack.priority(operStack.peek())){
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
result = numStack.cal(num1,num2,oper);
//把结果压入数栈
numStack.push(result);
operStack.push(ch);
}
//当前运算符的 优先级大于 栈中的运算符,直接压入符号栈
else {
operStack.push(ch);
}
}
else {
//为空,直接入栈
operStack.push(ch);
}
}
//如果是数字,直接放入数栈
else {
//注意:当多位数时需要连续入栈
//numStack.push(ch - 48);
//1.在处理数时,需要向expression表达式的index后再看一位
//2.如果是数就继续扫描,如果是符号就入栈
//keepNum字符变量用于拼接
keepNum += ch;
//如果ch已经是expression最后一位,就直接入栈
if (index == expression.length()-1){
numStack.push(Integer.parseInt(keepNum));
}
else {
//如果后一位是运算符
if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
numStack.push(Integer.parseInt(keepNum));
//注意:要清空keepNum
keepNum = "";
}
}
}
//index+1,判断是否扫描到expression最后
index++;
if (index >= expression.length()){
break;
}
}
//当扫描完毕,顺序弹出数栈、符号栈的数、符号,运算
while (true){
//当符号栈为空,计算到了最后的结果,数栈只有一个数字【结果】
if (operStack.isEmpty()){
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
result = numStack.cal(num1,num2,oper);
//把结果压入数栈
numStack.push(result);
}
System.out.println("表达式:"+expression+" = "+numStack.pop());
}
}
注意:输入为多位数字需要拼接为字符串(已实现)
结果:
总结
当前计算器完成的是中缀表达式的计算
中缀表达式是我们平时书写的表达式,但计算机并不好操作,通常会把中缀表达式转换为前缀表达式、后缀表达式
后续完成前缀表达式、后缀表达式