栈—计算器

本文介绍了如何使用栈数据结构实现中缀表达式的计算,包括加减乘除操作。首先讲解了中缀表达式的特点,然后通过Java代码展示了如何利用数组模拟栈来处理运算符的优先级,实现表达式的计算。接着,文章讨论了逆波兰表达式(后缀表达式)的计算方法,给出了详细的代码实现,说明了从后缀表达式到结果的计算过程。
摘要由CSDN通过智能技术生成

数组实现栈的功能

在这里插入图片描述

加减乘除 -中缀

  • 中缀表达式:一个通用的算术或逻辑公式表示方法。
    • 操作符是以中缀形式处于操作数的中间(例:3 + 4)
    • 与前缀表达式(例:+ 3 4)或后缀表达式(例:3 4 +)相比,中缀表达式不容易被计算机解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。
      在这里插入图片描述

代码实现

难点
  • 代码复刻语言逻辑
    在这里插入图片描述
  • 对char的认知
    在这里插入图片描述
  • 数组模拟栈
package stack;
/**
 * 定义一个ArrayStack实现栈功能
 *    利用数组表示栈
 *    栈的容量,栈顶
 *    栈满、栈空、pop、push、遍历、查看栈顶元素
 *
 * 计算机需要函数
 *   判断运算符优先级
 *   查看栈顶元素
 *   判断扫描字符串的当前字符是数字还是运算符
 *   计算方法
 */
public class ArrayStack {
    //容量
    private int maxSize;
    //数组表示栈
    private int[] stack;
    //栈顶
    private  int top=-1;
    //构造方法
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack=new int[maxSize];
    }
    //栈满
    public boolean isFull(){
        return top==maxSize-1;
    }
    //栈空
    public boolean isEmpty(){
        return top==-1;
    }
    //入栈
    public void push(int value){
        if(isFull()){
            System.out.println("栈满");
            return;
        }
        top++;
        stack[top]=value;
    }
    //出栈
    public int pop(){
        if(isEmpty()){
            //返回方法
            throw new RuntimeException("栈空,没有数据");
        }
        int value=stack[top--];
        return value;
    }
    //遍历
    public void list(){
        if(isEmpty()){
            System.out.println("栈空,没有数据!");
        }
        //从栈顶开始显示数据,即从数组的后端
        // 注意i的初始值
        for(int i=top;i>=0;i--){
            System.out.println(stack[i]);
        }
    }
    //查看栈顶元素
    public int checkTop(){
        return stack[top];
    }
    //判断优先级
    public  int priority(int oper){
        if(oper=='*'||oper=='/'){
            return 1;
        }else if(oper=='+'||oper=='-'){
            return 0;
        }else {
            //假设当前只有+-*/
            return -1;
        }
    }
    //计算方法
    public static  int cal(int num1,int num2,int oper){
        //res是暂时存放计算结果
        //这里默认num1先弹出栈,num2后弹出栈
        int res=0;
        switch (oper){
            case '+':
                res=num1+num2;
                break;
            case '-':
                //注意数字顺序和栈的取出顺序 以及计算式的逻辑 num2-num1
                res=num2-num1;
                break;
            case '*':
                res=num1*num2;
                break;
            case '/':
                res=num2/num1;
                break;
            default:
                break;
        }
        return res;
    }
    //判断扫描字符是运算符还是数字
    public boolean isOper(char value){
        return value=='+'||value=='-'||value=='*'||value=='/';
    }
}

  • 初步实现计算器功能
package stack;
/**
 * 利用栈的特性,实现表达式的计算
 *  中缀表达式
 *
 *  表达式: 3+2*6-2
 *思路分析
 *      1.通过一个 index值(索引),来遍历我们的表达式
 *      2如果我们发现是一个数字就直接入数栈——考虑到运算式是字符串,index一次识别一个字符
 * 所以当数字为多位的时候,需要拼接后,再入栈
 *      3.如果发现扫描到是一个符号,就分如下情况
 *           3.1如果发现当前的符号栈为空,就直接入栈
 *           3.2如果符号栈有操作符,就进行比较如果当前的操作符的优先级小于或者等于栈中的操作符,
 * 就需要从数栈中ρop出两个数在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,
 * 然后将当前的操作符入符号栈,如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
 *      4.当表达式扫描完毕,就顺序的从数栈和符号栈中op出相应的数和待号,并运行
 *      5.最后在数栈只有一个数字,就是表达式的结果
 *
 *
 */
public class Calculator {
    public static void main(String[] args) {
        //需要计算的表达式
        String expression="30+2*6-2";
        //建立数栈、符号栈
        ArrayStack numStack =new ArrayStack(10);
        ArrayStack operStack =new ArrayStack(10);
        //定义相关遍历
        int index =0;
        int num1=0;
        int num2=0;
        //拼接数字存储变量
        String joinNum ="";
        //为什么oper有时是char有时是int
            //答:两个char型运算时,自动转换为int型;当char与别的类型运算时,也会先自动转换为int型的,再做其它类型的自动转换
        int oper=0;
        int res=0;
        //index扫描的字符暂放到ch中存储
        char ch=' ';
        //扫描整个运算表达式,分别将数字和运算符push到对应的栈中
        while (true){
             //获取index指向的当前字符
            ch =expression.substring(index,index+1).charAt(0);
            //判断获取的字符是什么类型:数字;运算符
            if(operStack.isOper(ch)){
                //如果是运算符
                if(!operStack.isEmpty()){
                    //如果当前运算符栈非空,判断栈顶的运算符优先级和当前运算符的优先级大小
                    if(operStack.priority(ch)<=operStack.priority(operStack.checkTop())){
                        //栈顶运算符优先级大于未入栈的的运算符
                        //就需要从数栈中ρop出两个数在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,
                        //然后将当前的操作符入符号栈
                        num1=numStack.pop();
                        num2=numStack.pop();  //注意顺序
                        oper=operStack.pop();
                        res=ArrayStack.cal(num1,num2,oper);
                        numStack.push(res);
                        operStack.push(ch);
                    }else {
                        //当前运算符优先级大于栈顶优先级
                        operStack.push(ch);
                    }
                }else{
                    //当前运算符栈为空
                    operStack.push(ch);
                }
            }else{
                //如果是数字
                //numStack.push(ch);  错误,此时的ch是字符 不是数字
                //int num =Integer.valueOf(ch).intValue();
                    // char类型直接转换成int之后,得到的是它的ASCII。
                    // 错误 如果使用Inter转换,可以将char转为String再转换
                //numStack.push(ch-48);  正确 1的ascii码是49
                //numStack.push(ch-'0');  正确  十以内的加减乘除

                //处理多位数时,不能发现一个数就立即入栈,因为可能是多位数
                //此时,需要向expression表达式的index再看一位
                     // 如果是数字就拼接,如果是运算符就将拼接的数字入栈

                joinNum+=ch;
                if(index==expression.length()-1){
                    numStack.push(Integer.parseInt(joinNum));
                } else if(operStack.isOper(expression.substring(index+1,index+2).charAt(0))){
                    //如果下一个字符是运算符
                    numStack.push(Integer.parseInt(joinNum));
                    //将拼接的数存入栈,jionNum清空准备存储下一个
                    joinNum="";
                }
            }
            //字符串索引移位
            index++;
            //判断结束  索引大于等于表达式的长度,遍历完毕
            if(index>=expression.length()){
                break;
            }

        }
        //程序执行到此,遍历完运算表达式
        //计算两个栈中的数
        while(true){
            //结束条件
            if(operStack.isEmpty()){
                break;
            }
            num1=numStack.pop();
            num2=numStack.pop();
            oper=operStack.pop();
            res=ArrayStack.cal(num1,num2,oper);
            numStack.push(res);
        }
        int result =numStack.pop();
        System.out.println(expression+"="+result);
    }


}

逆波兰表达式

代码实现

package stack;
/**
 *  后缀表达式——逆波兰表达式
 *  首先将计算表达式转换成后缀表达式
 *例如:(3+4)×5-6对应的后缀表达式就是34+5×6-,针对后缀表达式求值步骤如下
 *   1.从左至右扫描,将3和4压入堆栈
 *   2.遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈
 *   3.将5入栈;
 *   4.接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈
 *   5.将6入栈;
 *   6.最后是运算符,计算出35-6的值,即29,由此得出最终结果
 */

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * 代码框架
 *      1、处理给定的逆波兰表达式字符串 :逆波兰表达式的数字和符号使用空格隔开
 *      2、因为使用空格分离,所以可以利用空格将字符串分离,并存放入String数值
 *      3、将String数组中的元素放到ArrayList集合
 *              因为提供处理逆波兰表达式的步骤不难发现,程序只需要遍历一次后缀表达式就可以得到结果
 *              此时存入ArrayList之后,问题就变成了使用栈和运算符遍历ArrayList就可以获得结果
 *      4、将 ArrayList传递给一个方法,遍历 ArrayList配合栈完成计算
 */
public class PolandNotation {
    public static void main(String[] args) {
        String suffiExperssion ="3 4 + 5 * 6 -";
        List list=getList(suffiExperssion);
        System.out.println(list);
        int res =calculate(getList(suffiExperssion));
        System.out.println(suffiExperssion+"运算结果="+res);
    }

    //处理逆波兰字符串
    public static List<String> getList(String experssion){
        //依据空格分离字符串
        String[] split = experssion.split(" ");
        List<String> list = new ArrayList<>();
        for(String ele:split){
            list.add(ele);
        }
        return list;
    }

    //对集合进行逆波兰表达式计算
    public static int calculate(List<String> ls){
        //栈中存储Integer类型
        Stack<Integer> stack = new Stack<>();
        for(String item:ls){
            //使用正则表达式——取数
                // \d 匹配一个数字(0到9)    + 表示 1个或多个
                //\\d+ 就表示多个数字,形如 12、44、6763……
            if(item.matches("\\d+")){
                stack.push(Integer.parseInt(item));
            }else{
                //如果遍历到的是运算符
                int num1=stack.pop();
                int num2=stack.pop();
                int res=0;
                //进行数字之间的运算,并把结果存入res
                if(item.equals("+")){
                    res=num1+num2;
                }else if(item.equals("-")){
                    //注意顺序
                    res=num2-num1;
                }else if(item.equals("*")){
                    res=num1*num2;
                }else if(item.equals("/")){
                    //注意顺序
                    res=num2/num1;
                }else{
                    throw new RuntimeException("输入符号有误");
                }
                stack.push(res);
            }
        }
        return stack.pop();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值