数据结构课程设计——项目3:算术表达式求解(中缀表达式转后缀表达式利用栈求解)

一、问题描述和项目要求

1.1 问题描述

    设计一个简单的算术表达式计算器。

1.2 基本要求

  1)实现标准整数类型的四则运算表达式的求值(包含括号,可多层嵌入)。
  2)要求自行设计非法表达式,进行程序测试,以保证程序的稳定运行。

二、问题分析

2.1 程序功能设计分析

  1)算数表达式中有效字符有:0~9、+、-、*、/、(、)
  2)运算符的优先级为乘=除>加=减,同优先级左大于右,
  3)应检查算术表达式左右括号是否对应匹配。
  4)应注意检查表达式中是否有非法字符,应注意0不能作为除数。
  5)程序可忽略中缀表达式中的所有空格,系统认为4 8+5为48+5,48 + 5 =为48+5=。

2.2 程序实现分析

 1. 输入的算数表达式例如 (30 + 2 * 70 )/ 3 - 12 * 3 是一个中缀表达式,即运算符在中间,操作数在运算符的两边,例中的2*70,*为运算符,2和70分别为左右操作数。对于中缀表达式,我们人能直观地看出算数表达式的含义进行求值,而计算机程序却难以理解中序表达式的含义。
 2. 而利用栈的先进先出结构,计算机能很好的处理后缀表达式。 例如 30 2 70 * + 3 / 12 3 * - 就是一个后缀表达式,后缀表达式两操作数在前,运算符在后,后续表达式不需要括号利用栈也能清楚地知道运算顺序。 如 12 3 *,12 和 3 为两个操作数,* 为运算符 。
  3. 因此对于输入的中缀表达式,只需将其转为后缀表达式,再利用栈就能求解得值。

三、逻辑设计

3.1 抽象数据类型的定义

程序整体的抽象数据类型定义

ADT Calculation
Data: 
中缀表达式
后缀表达式
运算符栈
操作数栈
Operation:
isNumber
   输入: 字符c
   输出: 无
   返回: 返回传入的字符c是否为数字0~9

isOperation
   输入: 字符c
   输出: 无
   返回: 返回传入的字符c是否为操作符

isLeftBrackets
   输入: 字符c
   输出: 无
   返回: 返回传入的字符c是否为左括号(中英文左括号均可)
   
isRightBrackets
   输入: 字符c
   输出: 无
   返回: 返回传入的字符c是否为右括号(中英文左括号均可)
   
comparator// 比较两运算符优先级
   输入: ch1栈顶运算符,ch2遍历到的运算符
   输出: 无
   返回: 返回1表示ch1>ch2,返回-1表示ch1<ch2,返回0表示ch1=ch2

translate// 将中缀表达式转换为后缀表达式
   输入: 无
   输出: 无
   返回: 返回转换是否成功,若表达式不合规范返回false

calculate// 利用后缀表达式计算算数表达式
   输入: 无
   输出: 计算结果
   返回: 返回计算结果

check// 初步检查算数表达式是否符合规范
   输入: 无
   输出: 无
   返回: 返回true表示算数表达式符合规范,反之不符合规范
endADT

3.2 程序流程图

程序流程图

四、主要方法的算法设计

4.1 translate方法

  • 作用:将输入的中缀表达式转换未后缀表达式

  • 算法思路(这里仅对于一个符合规范的中缀表达式,要求中缀表达式末尾以=结束):
      1) 初始化一个存放操作符的栈,将一个‘=’入栈(这里可不放入,放入是方便后面的处理),初始化后缀表达式为空字符串,初始化count=0用于记录扫描到的左括号数
      2) 顺序读取中缀表达式
        2.1 若读取到的为操作数,直接加入后缀表达式末尾。
        2.2 若读取到的为操作符记为ch2,若栈大小为1,即栈中只有起始的=,ch2直接入栈,若栈大小大于1,即栈中有除了起始的=以外其余的字符,记栈顶元素为ch1,比较ch1与ch2的优先级,优先级为左括号>乘=除>加=减>右括号,同优先级的中缀表达式中左边的>右边的(也即栈中的大于新扫描到的),特别处理左括号优先级与右括号相同。
          2.2.1 若ch1<ch2,则ch2进栈,继续顺序读取中缀表达式。
          2.2.2 若ch1>ch2,则栈顶元素ch1出栈加入后缀表达式末尾,继续比较新的栈顶元素ch1’ 与ch2优先级,遵循2.2.1和2.2.2的处理过程。
        2.3 若读取到的为左括号,直接入栈(按照优先级比较结果也是这样,这里可简化比较优先级的过程),count++。
        2.4 若读取到的为右括号,如果count<1,则右括号没有匹配到左括号,返回false表示括号匹配失败;如果count>=1(该右括号匹配到了1个左括号),count–,栈顶元素持续出栈并加入后缀表达式末尾直到栈顶元素为左括号, 将左括号出栈但不加入后缀表达式,继续顺序读取中缀表达式。
        2.5 若读取到的为**=**,说明顺序表达式已读取完毕,若此时count>0,说明还有左括号仍未被匹配,返回false表示括号匹配失败。若count==0,说明所有括号都匹配成功,**将栈顶元素持续出栈并加入后缀表达式末尾直到栈中只有一个元素。**将=号也出栈,后缀表达式以转换完毕。最后返回true表示所有括号均已匹配成功。

  • 方法源码如下:

    // 用到的变量如下:
    String inOrder;// 中缀表达式
    String postOrder;// 后缀表达式
    // 运算符栈
    Stack<Character> opreationStack = new Stack<Character>();


    /**
     * 将中缀表达式转换为后缀表达式
     * @return 返回转换是否成功, 只能检测所有括号是否对应匹配
     */
    boolean translation() {
   
        opreationStack.push('=');
        String s = "";
        int count = 0;// 记录操作符栈中左括号个数
        for (int i = 0; i < inOrder.length(); i++) {
   
            char ch2 = inOrder.charAt(i);
            if (ch2 == ' ') {
   
                continue;
            } else if (isNumber(ch2)) {
   
                s = s.concat(String.valueOf(ch2));
            } else if (isOperation(ch2)) {
   
                if (s.length() > 0) {
   
                    postOrder = postOrder.concat(s + " ");
                    s = "";
                }
                int c;
                do {
   
                    char ch1 = opreationStack.peek();
                    c = comparator(ch1, ch2);
                    if (c == 1) {
   
                        postOrder = postOrder.concat(String.valueOf(ch1) + " ");
                        opreationStack.pop();
                    }
                } while (opreationStack.size() > 1 && c == 1);

                // 栈空或ch1<ch2
                opreationStack.push(ch2);

            } else if (isLeftBrackets(ch2)) {
   // 左操作符直接入栈
                opreationStack.push(ch2);
                count++;
            } else if (isRightBrackets(ch2)) {
   
                if (count < 1) {
   // 没匹配到左括号
                    System.out.println("括号未匹配");
                    return false;
                } else {
   // 能匹配到左括号
                    if (s.length() > 0) {
   
                        postOrder = postOrder.concat(s + " ");
                        s = "";
                    }
                    int c;
                    do {
   
                        char ch1 = opreationStack.peek();
                        c = comparator(ch1, ch2);
                        if (c != 0) {
   // 一直循环遇到左括号为止
                            postOrder = postOrder.concat(String.valueOf(ch1) + " ");
                            /*    System.out.println(opreationStack.pop());*/
                            opreationStack.pop();
                        }
                    } while (opreationStack.size() > 1 && c != 0);
                    opreationStack.pop();
                    count--;
                }

            } else if (ch2 == '=') {
   
                if (count > 0) {
   
                    System.out.println("括号未匹配");
                    return false;
                }
                if (s.length() > 0) {
   
                    postOrder = postOrder.concat(s + " ");
                    s = "";
                }
                while (opreationStack.size() > 1) {
   
                    char ch1 = opreationStack.peek();
                    postOrder = postOrder.concat(String.valueOf(ch1) + " ");
                    opreationStack.pop();
                }
                opreationStack.pop();
            }
        }
        return 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值