递归算法一般需要利用栈实现_递归算法的结构

递归算法一般需要利用栈实现_递归算法的结构一、计算器的计算思路分析我们以计算3+8*2-6这个算式为例:将算式解析为数字和符号:3,+,8,*,2,-,6准备一个用于存放数字的数字栈numStack,还有一个存放运算符号的符号栈symb

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、计算器的计算思路分析

我们以计算3+8*2-6这个算式为例:

  1. 将算式解析为数字和符号:3,+,8,*,2,-,6

  2. 准备一个用于存放数字的数字栈numStack,还有一个存放运算符号的符号栈symbolStack,下面分别简称栈n和栈s

  3. 按顺序扫描解析后的数字和符号,

    如果是数字,就直接入数栈n,

    如果是符号,且如果符号栈s为空,就直接入栈,

    如果s不为空,就需要比较栈顶符号与当前符号的优先级,再分两种情况:

    • 如果栈顶符号优先级比当前符号大,就从栈n弹出两个数字,从栈s弹出一个符号,然后进行运算,最后得出的结果再入栈n
    • 如果栈顶符号优先级小于或等于当前符号,就将符号入栈s

递归算法一般需要利用栈实现_递归算法的结构

按照这个流程,扫描完后栈n会留下3,16,6这三个数,栈s会留下+,-这两个 符号,

然后按顺序,栈n弹出两个数字,栈s弹出一个符号,然后运算得到结果后再入栈n,重复此步骤,最后栈s清空,栈n只剩一个数字,即为运算结果。

二、代码实现

我们先来实现一个加减乘除的数计算器:

/**
 * @Author:黄成兴
 * @Date:2020-06-25 16:29
 * @Description:使用栈实现一个计算器
 */
public class StackCalculator {

    //数字栈
    public Stack<Integer> numStack = new Stack<>();

    //符号栈
    public Stack<String> symbolStack = new Stack<>();

    /**
     * 根据计算公式运算并输出结果
     * @param expression 计算公式
     */
    public void calculate(String expression) {
        //用于多位数拼接
        String numStr = "";

        //遍历字符串里的每一个字符
        for (int i = 0; i < expression.length() ; i++) {
            String ch = expression.substring(i, i + 1);
            
            //检验是否数字
            if (ch.matches("[0-9]")) {
                //如果该数字已经是最后一个字符就直接存入数字栈
                if (i == expression.length() - 1){
                    numStack.push(Integer.valueOf(ch));
                }else {
                    //如果不是字符串最后一个字符,就拼接入字符串
                    numStr += ch;
                }
                continue;
            }
            //如果是符号,就把之前拼接的多位数存入数字栈
            numStack.push(Integer.valueOf(numStr));
            numStr = "";
            
            //如果是符号就比较符号优先级
            if (isFrist(ch)){
                //如果当前符号与符号栈栈栈顶符号优先或者平级就入栈
                symbolStack.push(ch);
            }else {
                //否则就从栈中取两数字和一个符号先计算
                int num = getCalculateResult();
                //再把计算结果入数栈
                numStack.push(num);
                //再把当前符号入栈
                symbolStack.push(ch);
            }
        }

        //当符号栈为空时,说明计算完成,此时数字栈唯一数字即为结果
        while (!symbolStack.empty()) {
            int result = getCalculateResult();
            numStack.push(result);
        }
        System.out.println("计算结束!结果为:" + numStack.pop());
    }
    
    /**
     * 根据数字和符号运算并返回结果
     * @param num1 后出栈的数字
     * @param num2 先出栈的数字
     * @param symbol 运算符号
     * @return
     */
    public int getCalculateResult(int num1, int num2, String symbol) {
        int result = 0;
        //根据符号进行运算
        switch (symbol){
            case "+":
                result = num1 + num2;
                break;
            case "-":
                result = num1 - num2;
                break;
            case "*":
                result = num1 * num2;
                break;
            case "/":
                result = num1 / num2;
                break;
                default:
                    throw new RuntimeException("只支持加减乘除运算!");
        }

        System.out.println(num1 + symbol + num2 + "=" + result);

        return result;
    }

    /**
     * 直接从数栈取两数字,符号栈取一符号进行计算
     * @return
     */
    public int getCalculateResult() {
        int num1 = numStack.pop();
        int num2 = numStack.pop();
        String symbol = symbolStack.pop();
        int result = getCalculateResult(num2, num1, symbol);
        return result;
    }

    /**
     * 比较符号和当前符号栈顶符号的优先级。
     * 如果当前符号优先级小于符号栈栈顶符号的优先级,就返回false,否则返回true
     * 如果当前符号栈为空,直接返回true
     * @param symbol
     * @return
     */
    public boolean isFrist(String symbol) {
        //判断当前符号栈是否为空
        if (symbolStack.empty()) {
            return true;
        }
        //取出并放回栈顶符号
        String stackSymbol = symbolStack.pop();
        symbolStack.push(stackSymbol);

        //栈顶符号的优先级
        int stackSymbolGrade = getSymbolGrade(stackSymbol);
        //当前符号的优先级
        int symbolgrade = getSymbolGrade(symbol);

        return symbolgrade > stackSymbolGrade;
    }

    /**
     * 根据符号返回符号的优先级
     * @param symbol
     * @return 加减返回0,乘除返回1
     */
    public int getSymbolGrade(String symbol) {
        int grade;
        if ("+".equals(symbol) || "-".equals(symbol)) {
            grade = 0;
        } else if ("*".equals(symbol) || "/".equals(symbol)) {
            grade = 1;
        }else {
            throw new RuntimeException("不支持的操作符类型:" + symbol);
        }
        return grade;
    }
}

现在输入运算公式调用calculate()函数即可得出结果,但是目前这个计算器仍然还有致命问题没有解决:

当连续乘除时无法识别,比如:2*3*3+3会被识别为(2*3)*(3+3)

这个问题下面我们将用递归来解决。

三.、使用递归解决连乘问题

我们分析主函数calculate()中关于比较符号的代码片段:

//如果是符号就比较符号优先级
if (isFrist(ch)){
    //如果当前符号与符号栈栈栈顶符号优先或者平级就入栈
    symbolStack.push(ch);
}else {
    //否则就从栈中取两数字和一个符号先计算
    int num = getCalculateResult();
    //再把计算结果入数栈
    numStack.push(num);
    //再把当前符号入栈
    symbolStack.push(ch);
}

可以知道主要问题在于运算符的比较只能进行一次,实际上可能会有连需乘除的情况。

举个例子,要计算1*2*3*4+3,+入栈前,数字栈有1234,符号栈有三个*:

  1. 加号入栈前,取出第一个乘号比较,发现乘法优先,于是取出4和3乘后得12,把12入数栈
  2. 此时数栈有1,2和12,符号栈有两个*,然后重复步骤1过程,再把乘号取出一个进行比较……
  3. 步骤2结束后数字栈有1和24,符号栈还有一个*,于是再重复步骤1过程…..
  4. 最终,符号栈没有比+更优先的符号了,于是加号入栈

以此类推,无论有多少个乘号,实际上的代码都是重复执行步骤1,直到满足进入步骤4的条件时结束

如果我们把步骤1提取成一个函数,让他执行结束后再调用自己,有几个乘号就让他自己调用几次,那么等到满足步骤4的条件时,也就说明套娃到底了,这时就可以结束调用返回结果。

按照这个思路,我们把原先的代码提取成一个递归方法:

/**
 * 使用递归解决连乘或连除问题
 * @param symbol
 */
private void compareAndOperation(String symbol) {
    //如果是符号就比较符号优先级
    if (isFrist(symbol)){
        //如果当前符号与符号栈栈栈顶符号优先或者平级就入栈
        symbolStack.push(symbol);
        return;
    }else {
        //否则就从栈中取两数字和一个符号先计算
        int num = getCalculateResult();
        //再把计算结果入数栈
        numStack.push(num);

        //递归,继续比较上一个是否与当前符号的优先级
        compareAndOperation(symbol);
    }
}

现在就能计算连续乘除的情况了!

四.完整代码

/**
 * @Author:黄成兴
 * @Date:2020-06-25 16:29
 * @Description:使用栈实现一个计算器
 */
public class StackCalculator {

    //数字栈
    public Stack<Integer> numStack = new Stack<>();

    //符号栈
    public Stack<String> symbolStack = new Stack<>();

    /**
     * 根据计算公式运算并输出结果
     * @param expression 计算公式
     */
    public void calculate(String expression) {
        //用于多位数拼接
        String numStr = "";

        //遍历字符串里的每一个字符
        for (int i = 0; i < expression.length() ; i++) {
            String ch = expression.substring(i, i + 1);
            //检验是否数字
            if (ch.matches("[0-9]")) {
                //如果该数字已经是最后一个字符就直接存入数字栈
                if (i == expression.length() - 1){
                    numStack.push(Integer.valueOf(ch));
                }else {
                    //如果不是字符串最后一个字符,就拼接入字符串
                    numStr += ch;
                }
                continue;
            }
            //如果是符号,就把之前拼接的多位数存入数字栈
            numStack.push(Integer.valueOf(numStr));
            numStr = "";

            //如果是符号就比较符号优先级并进行计算和入栈操作
            compareAndOperation(ch);
        }

        //当符号栈为空时,说明计算完成,此时数字栈唯一数字即为结果
        while (!symbolStack.empty()) {
            int result = getCalculateResult();
            numStack.push(result);
        }
        System.out.println("计算结束!结果为:" + numStack.pop());
    }

    /**
     * 使用递归解决连乘或连除问题
     * @param symbol
     */
    private void compareAndOperation(String symbol) {
        //如果是符号就比较符号优先级
        if (isFrist(symbol)){
            //如果当前符号与符号栈栈栈顶符号优先或者平级就入栈
            symbolStack.push(symbol);
            return;
        }else {
            //否则就从栈中取两数字和一个符号先计算
            int num = getCalculateResult();
            //再把计算结果入数栈
            numStack.push(num);

            //递归,继续比较上一个是否与当前符号的优先级
            compareAndOperation(symbol);
        }
    }

    /**
     * 根据数字和符号运算并返回结果
     * @param num1 后出栈的数字
     * @param num2 先出栈的数字
     * @param symbol 运算符号
     * @return
     */
    public int getCalculateResult(int num1, int num2, String symbol) {
        int result = 0;
        //根据符号进行运算
        switch (symbol){
            case "+":
                result = num1 + num2;
                break;
            case "-":
                result = num1 - num2;
                break;
            case "*":
                result = num1 * num2;
                break;
            case "/":
                result = num1 / num2;
                break;
                default:
                    throw new RuntimeException("只支持加减乘除运算!");
        }

        System.out.println(num1 + symbol + num2 + "=" + result);

        return result;
    }

    /**
     * 直接从数栈取两数字,符号栈取一符号进行计算
     * @return
     */
    public int getCalculateResult() {
        int num1 = numStack.pop();
        int num2 = numStack.pop();
        String symbol = symbolStack.pop();
        int result = getCalculateResult(num2, num1, symbol);
        return result;
    }

    /**
     * 比较符号和当前符号栈顶符号的优先级。
     * 如果当前符号优先级小于符号栈栈顶符号的优先级,就返回false,否则返回true
     * 如果当前符号栈为空,直接返回true
     * @param symbol
     * @return
     */
    public boolean isFrist(String symbol) {
        //判断当前符号栈是否为空
        if (symbolStack.empty()) {
            return true;
        }
        //取出并放回栈顶符号
        String stackSymbol = symbolStack.pop();
        symbolStack.push(stackSymbol);

        //栈顶符号的优先级
        int stackSymbolGrade = getSymbolGrade(stackSymbol);
        //当前符号的优先级
        int symbolgrade = getSymbolGrade(symbol);

        return symbolgrade > stackSymbolGrade;
    }

    /**
     * 根据符号返回符号的优先级
     * @param symbol
     * @return 加减返回0,乘除返回1
     */
    public int getSymbolGrade(String symbol) {
        int grade;
        if ("+".equals(symbol) || "-".equals(symbol)) {
            grade = 0;
        } else if ("*".equals(symbol) || "/".equals(symbol)) {
            grade = 1;
        }else {
            throw new RuntimeException("不支持的操作符类型:" + symbol);
        }
        return grade;
    }
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170815.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • html跳转指定位置(html登录页面跳转到不同页面)

    锚标签和href属性HTML使用(锚)标签来创建连接另一个文档的链接。锚可以指向网络上的任何资源:一张HTML页面,一幅图像,一个声音或视频文件等等。用来创建锚。href属性用于定位需要链接的文档,锚的开始标签和结束标签之间的文字被作为超级链接来显示。锚标签和Name属性Name属性用于创建被命名的锚(namedanchors)。当使用命名锚(name

  • 一次List对象去重失败,引发对Java8中distinct()的思考

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:puppylpg blog.csdn.net/puppylpg/article/details/7855673…

  • 普通大一学生的自我反思[通俗易懂]

    普通大一学生的自我反思[通俗易懂]​ 暑假高考完,得知自己被计科(普通本科)录取,便开始在知乎等地方搜索相关知识,以此来提高自己的认识。​ 先是在b站上跟着比特鹏哥学完了c语言(基础),这里又要有一大段故事了,我家在江苏扬州,大概是9月份突然爆发了疫情,就被关在家里不让出去,(在家里坐牢,核酸检测,基本隔一天就要做一次)就在B站上天天看鹏哥学c语言,一开始有新鲜劲,学起来还是很有动力的,后来学到指针,很是痛苦,就又专门去B站听了其他的指针教程,学着学着慢慢就明白了,当时每天的生活节奏基本就是,起床打开电脑学习c语言,学饿了,吃点饭(由于疫

  • Mac的pycharm中快捷键command+a不能全选「建议收藏」

    Mac的pycharm中快捷键command+a不能全选「建议收藏」问题:今天在pycharm中想使用快捷键全选结果:command+a的时候不是全选而是showusages原因:是因为安装了ideavim插件解决方法:卸载掉ideavim插件找到这个插件ideavim卸载uninstall卸载之后提示重启,重启就ok了。…

  • 新的历程-近两个月的工作总结

    新的历程-近两个月的工作总结

    2021年11月15日
  • Mybatis 一对多关联查询collection用法[通俗易懂]

    Mybatis 一对多关联查询collection用法[通俗易懂]使用resultMap,select标签,resultMap的中的collection表示一对多,column对应select标签中的sql里的字段或者别名,当两个表字段名称有相同的情况下,可以定义别名。<resultMapid=”authorWorksInfo”type=”package.vo.AuthorWorksInfo”><idcolumn=”id”property=”id”/><resultcolumn=”name”pro…

    2022年10月30日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号