【Java】构建表达式二叉树和表达式二叉树求值

news2024/12/28 6:04:39

问题背景

1. 实现一个简单的计算器。通过键盘输入一个包含圆括号、加减乘除等符号组成的算术表达式字符串,输出该算术表达式的值。要求:

(1)系统至少能实现加、减、乘、除等运算;

(2)利用二叉树算法思想求解表达式的值,先构造由表达式构成的二叉树,按中序、后序遍历的方式输出二叉树中的结点,然后再利用通过对二叉树进行后序遍历求解算术表达式的值。

思路描述

  • 构建表达式二叉树:先用栈将算术表达式转成后缀表达式(具体思路参考),再根据后缀表达式构建表达式二叉树,构建过程:从左到右扫描后缀表达式的每个元素:如果当前元素是操作数,则创建一个只包含该操作数的节点,并将该节点压入栈中;如果当前元素是操作符,则创建一个只包含该操作符的节点,并从栈中弹出两个节点作为其左右子节点,再将该节点压入栈中。最后栈中唯一的节点即为根节点。
  • 表达式二叉树求值:后序遍历二叉树,递归计算左右子树的值,代入运算符计算

 代码实现

//二叉链表存储二叉树
public class TreeNode {
    String value;
    TreeNode left;
    TreeNode right;
    public TreeNode(String value){
        this.value=value;
    }

    public TreeNode(String value, TreeNode left, TreeNode right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }
}
public class Calculator {
    private TreeNode root;
    public Calculator() {
        root = null;
    }

    public Calculator(TreeNode root) {
        this.root = root;
    }

    //中缀转后缀,List中每个元素就是后缀表达式中每个操作数或运算符
    public static List<String> infixToPostfix(String infixExpression){
        Stack<Character> operatorStack=new Stack<>();
        List<String> postfixExpression=new ArrayList<>();
        for(int i=0;i<infixExpression.length();i++){
            //如果是数字
            if(Character.isDigit(infixExpression.charAt(i))){
                //可能是多位的数
                StringBuilder temp=new StringBuilder();
                temp.append(infixExpression.charAt(i));
                while (++i<infixExpression.length()&&Character.isDigit(infixExpression.charAt(i))){
                    temp.append(infixExpression.charAt(i));
                }
                postfixExpression.add(temp.toString());
                i--;//while判断完后,i多往后挪了一位,所以要-1
            }//如果是左括号
            else if(infixExpression.charAt(i)=='('){
                operatorStack.push(infixExpression.charAt(i));
            }//如果是右括号,去匹配左括号
            else if(infixExpression.charAt(i)==')'){
                while (!operatorStack.empty()&&operatorStack.peek()!='('){
                    postfixExpression.add(operatorStack.pop()+"");
                }

                operatorStack.pop();
            }
            //如果是+-*/
            else {
                //将优先级<=当前运算符优先级的运算符pop出来,追加到后缀表达式中
                while (!operatorStack.empty()&&getPrecedence(infixExpression.charAt(i))<=getPrecedence(operatorStack.peek())){
                    postfixExpression.add(operatorStack.pop()+"");
                }
                operatorStack.push(infixExpression.charAt(i));
            }
        }
        //将栈中剩余的运算符依次pop出来追加到结果中
        while (!operatorStack.empty()){
            postfixExpression.add(operatorStack.pop()+"");
        }
        return postfixExpression;
    }
    //在中缀转后缀时要判断符号优先级
    private static int getPrecedence(char operator) {
        switch (operator) {
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
            case '%':
                return 2;
            default:
                return 0;
        }
    }
    //利用后缀表达式构建表达式二叉树
    public static TreeNode buildExpressionTree(List<String> postfixExpression){
        Stack<TreeNode> stack=new Stack<>();
        //从左至右遍历后缀表达式
        for(String str:postfixExpression){
            //如果是运算数
            if(str.charAt(0)>=48&&str.charAt(0)<=57){
                TreeNode treeNode = new TreeNode(str, null, null);
                stack.push(treeNode);
            } else {//如果是运算符
                //从栈中弹出两个节点
                TreeNode pop1 = stack.pop();
                TreeNode pop2 = stack.pop();
                TreeNode treeNode = new TreeNode(str, pop1, pop2);
                stack.push(treeNode);
            }
        }
        //最后栈中剩余的节点就是二叉树根节点
        return stack.peek();
    }
    //中序遍历表达式二叉树(左根右)
    public static void inorderTraversal(TreeNode treeNode){
        if(treeNode==null)
            return;
        inorderTraversal(treeNode.left);
        System.out.print(treeNode.value+" ");
        inorderTraversal(treeNode.right);
    }
    //后序遍历表达式二叉树(左右根)
    public static void postorderTraversal(TreeNode treeNode){
        if(treeNode==null)
            return;
        postorderTraversal(treeNode.left);
        postorderTraversal(treeNode.right);
        System.out.print(treeNode.value+" ");
    }
    //后序遍历表达式二叉树求值
    public int evaluateExpression(){
        return evaluateExpression(root);
    }
    public int evaluateExpression(TreeNode root){
        if(root==null){
            return 0;
        }
        // 递归计算左右子树的值
        int leftValue = evaluateExpression(root.left);
        int rightValue = evaluateExpression(root.right);
        switch (root.value){
            case "+":
                return leftValue+rightValue;
            case "-":
                return leftValue-rightValue;
            case "*":
                return leftValue*rightValue;
            case "/":
                return leftValue/rightValue;
            default:
                // 如果是操作数,则返回对应的整数值
                return Integer.valueOf(root.value);
        }
    }
}

 运行效果

public class Main {


    public static void main(String[] args) {
        System.out.println("请输入你要计算的表达式:");
        Scanner sc = new Scanner(System.in);
        String infixExpression = sc.next();
        List<String> postfixExpression = Calculator.infixToPostfix(infixExpression);//中缀转后缀
        TreeNode binaryTree = Calculator.buildExpressionTree(postfixExpression);//利用后缀表达式构建表达式二叉树
        System.out.print("中序遍历:");
        Calculator.inorderTraversal(binaryTree);//中序遍历
        System.out.println();
        System.out.print("后序遍历:");
        Calculator.postorderTraversal(binaryTree);//后序遍历
        System.out.println();
        Calculator calculator = new Calculator(binaryTree);
        int i = calculator.evaluateExpression();
        System.out.println("值为:"+i);
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1304940.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode(55)环形链表【链表】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 环形链表 1.题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评…

啊?150水冷踏板卷到7千多,巧格的钱购买150了?

力帆的车一般我是不太想写的&#xff0c;但是顶不住它这个价格&#xff0c;实在是....&#xff0c;标准版售价干到了7980元&#xff0c;和巧格一个价了&#xff0c;比福喜还便宜点&#xff0c;属实是离离原上谱&#xff0c;不过这个车不太影响的了豪爵大哥的UHR&#xff0c;两台…

Cypress安装与使用教程(2)—— 软测大玩家

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

pip list 报错 ImportError: cannot import name ‘main‘ from ‘pip._int

文章目录 报错信息问题原因解决方案 关注公众号&#xff1a;『AI学习星球』 算法学习、4对1辅导、论文辅导或核心期刊可以通过公众号或CSDN滴滴我 报错信息 最近在操作服务器的时候&#xff0c;发现pip list这个命令不好使了&#xff0c;报错如下 外链图片转存失败,源站可能…

[Linux] yum安装分布式LNMP架构

1. 在一台主机安装nginx&#xff08;192.168.136.120&#xff09; 1.1 搭建nginx相关的yum源 cd /yum.repos.d mkdir bak mv *.repo bak vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/7/$basearch/ gpgche…

Self-Distillation from the Last Mini-Batch for Consistency Regularization中文版

Self-Distillation from the Last Mini-Batch for Consistency Regularization 从上一个小批量自发蒸馏&#xff0c;实现一致性正则化 摘要 知识蒸馏&#xff08;Knowledge distillation&#xff0c;KD&#xff09;展示了强大的潜力&#xff0c;作为一种强有力的正则化策略&a…

CETN01 - How to Use Cloud Classroom

文章目录 I. Introduction to Cloud ClassroomII. How to Use Cloud Classroom1. Publish Resources2. Conduct Activities3. Class Teaching Reports4. View Experience Values5. Performance in Cloud Classroom I. 云课堂介绍II. 如何使用云课堂1. 发布资源2. 进行活动3. 班…

C++STL之List的实现

首先我们要实现List的STL,我们首先要学会双向带头链表的数据结构。那么第一步肯定是要构建我们的节点的数据结构。 首先要有数据域&#xff0c;前后指针域即可。 再通过模板类进行模板化。 然后再写List的构造函数&#xff0c;这个地方用T&,通过引用就可以减少一次形参拷…

Android 蓝牙BluetoothAdapter 相关(一)

Android 蓝牙相关 本文主要讲述android 蓝牙的简单使用. 1: 是否支持蓝牙 /*** 是否支持蓝牙** return*/ private boolean isSupportBluetooth() {BluetoothAdapter bluetoothAdapter BluetoothAdapter.getDefaultAdapter();return bluetoothAdapter ! null; }2: 开启蓝牙 …

强大的音频编辑器 Metadatics直装 for mac

Metadatics是一款Mac上的音频元数据编辑器&#xff0c;功能强大且高级。它支持批量编辑最常见的音频文件类型&#xff0c;包括MP3、M4A、AIFF、WAV、FLAC、APE、OGG、WMA等。它可以从在线资源中查找元数据&#xff0c;根据元数据重命名文件&#xff0c;或使用众多内置函数之一来…

Mysql、Oracle安全项检查表及操作脚本

软件开发全资料获取&#xff1a;点我获取 Mysql检查表 Oracle检查表

【Canvas】记录一次从0到1绘制风场空间分布图的过程

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热衷分享有趣实用的文章&#xff0c;希望大家多多支持&#xff0c;一起进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 背景 前置知识 风场数据 绘制风场 准备工作 生成二维网格 获取…

ppt转换成pdf文件

最近用到了&#xff0c;记一下&#xff1b; ppt转pdf分为两种情况: 小于2007版本的 .ppt格式&#xff08;2003&#xff09; 与大于2007版本的 .pptx格式&#xff08;2007&#xff09; .ppt格式为 二进制文件 .pptx格式为xml格式&#xff0c;在java中有不同的jar包需要使用 引入…

MacOS 12 开放指定端口 指定ip访问

MacOS 12 开放指定端口 指定ip访问 在 macOS 上开放一个端口&#xff0c;并指定只能特定的 IP 访问&#xff0c;你可以使用 macOS 内置的 pfctl&#xff08;Packet Filter&#xff09;工具来实现。 以下是一些基本的步骤&#xff1a; 1、 编辑 pf 配置文件&#xff1a; 打开 /…

Dockerfile创建镜像--LNMP+wordpress

实验准备&#xff1a; nginx&#xff1a;172.111.0.10 docker-nginx mysql&#xff1a;172.111.0.20 docker-mysql php&#xff1a;172.111.0.30 docker-php 自定义网段&#xff1a;172.111.0.0/16mkdir nginx mysql php mv nginx-1.22.0.tar.gz wordpress-6.4.2-zh_CN.ta…

数据结构之选择排序

目录 直接选择排序 选择排序的时间复杂度 堆排序 向上调整算法 向下调整算法 向上调整算法建立堆 向下调整算法建立堆 堆排序整体代码 堆排序的时间复杂度 直接选择排序 在之前讲插入排序时&#xff0c;我们讲了这样的一个应用场景&#xff0c;我们在斗地主摸牌时&…

PyInstaller 打包 Python 脚本为 .exe 可执行文件闪退、No Model named XXX问题

文章目录 前言.exe 可执行文件闪退No Model named XXXPython 环境问题查看当前python路径查看当前python环境使用的site-package路径 个人简介 前言 在上一篇文章中&#xff0c;我们介绍了如何将 Python 脚本打包为 .exe 可执行文件&#xff0c;但有时候打包生成的 .exe 文件会…

慢SQL诊断

最近经常遇到技术开发跑来问我慢SQL优化相关工作&#xff0c;所以干脆出几篇SQL相关优化技术月报&#xff0c;我这里就以公司mysql一致的5.7版本来说明下。 在企业中慢SQL问题进场会遇到&#xff0c;尤其像我们这种ERP行业。 成熟的公司企业都会有晚上的慢SQL监控和预警机制。…

阿里云cdn设置相同的域名路径访问不同的oss目录

1.设置回源配置&#xff0c;添加回源URL改写 2.设置跨域&#xff0c;cdn的跨域优先oss 3.回源设置

前端 三种解决跨域问题 jsonp 、CORS、代理服务器 解决跨域全家桶

我的报错情况是 后端接口是3000 前端本地接口是8080&#xff0c;最后出现跨域 1.什么是跨域&#xff1f; 首先跨域是一种安全机制&#xff0c;是在开发上线前考虑到的安全问题并且需要采取合适的手段去避免这个问题带来的程序错误,接口跨域可以后端处理,也可以前端处理&#x…