JAVA数据结构与算法-前缀、中缀、后缀表达式


例:计算(3+4)*5-6。

一.前缀表达式(波兰表达式):

表达式为:-*+3456。

计算机求值步骤:从右向左扫描,遇到数字时压入堆栈,遇到运算符时弹出栈顶的两个数字,使用运算符进行运算(若为-则计算表达式为栈顶元素-次顶元素),并将结果入栈,一直进行到表达式最左端。

运算顺序:先按顺序将所有数字入栈,再根据表达式的计算顺序逐个运算符计算。

二.中缀表达式:

与平常顺序相同,为(3+4)*5-6,对计算机来说不好操作,需要判断计算优先级,通常会转为其他表达式。

三.后缀表达式(逆波兰表达式):

表达式为34+5*6-。

计算机求值步骤:与前缀表达式相似,运算符位于操作符之后。计算时从左向右扫描,扫描到数字则入栈,扫描到操作符则弹出栈顶的两个元素进行运算(若为-则计算表达式为次顶元素-栈顶元素),并将结果入栈,一直到表达式最右端。

特点:若出现多个数字间无操作符,则代表有小括号;数字与中缀表达式顺序一致;通常在两个数字之后会有一个操作符表示这两个数字的操作。

实例:

                 正常表达式                                                           逆波兰表达式

                       a+b                                                                       ab+

                    a+(b-c)                                                                   abc-+

                   a+(b-c)*d                                                                adbc-*+

                    a=1+3                                                                    a13+=

四.逆波兰计算器

class PolandCalculator {
    public ArrayList<String> arr;
    public Stack<Integer> stack = new Stack<>();  

    public boolean isDigit(String sub) {  // 判断该字符串是否全为数字,返回值为boolean
        boolean flag = true;
        for (int i = 0; i < sub.length(); i++) {
            char ch = sub.charAt(i);
            if (ch > 57 || ch < 48) {
                flag = false;
                break;
            }
        }
        return flag;
    }

    public int cal(int num1, int num2, int operate) {  // 定义将运算符和数pop后计算的过程,输入均为int,运算符以char类型保存,其int值为ASCII码值,输出为计算结果
        int res = 0;
        switch (operate) {
            case '+' -> res = num2 + num1;
            case '-' -> res = num2 - num1;
            case '*' -> res = num2 * num1;
            case '/' -> res = num2 / num1;
            default -> {
            }
        }
        return res;
    }

    public int calculate(String expression) {  // 计算表达式的值,输入为字符串类型的表达式,各数字与运算符间使用空格分隔
        arr = new ArrayList<>();
        String[] str = expression.split(" ");
        arr.addAll(Arrays.asList(str));  // 字符串数组转ArrayList
        for (String s : arr) {
            if (isDigit(s)) stack.push(Integer.parseInt(s));  // 若为数字则入栈
            else {  // 若为运算符则弹出两个数,进行运算后入栈
                int num1 = stack.pop();
                int num2 = stack.pop();
                int res = cal(num1, num2, s.charAt(0));
                stack.push(res);
            }
        }
        return stack.pop();
    }
}

五.中缀表达式转后缀表达式

1.步骤

  1. 初始化两个栈:s1存储运算符,s2存储中间结果。

  2. 从左至右扫描中缀表达式。

  3. 遇到数字时压入s2.

  4. 遇到运算符时,比较其与s1栈顶运算符的优先级
    4.1若s1为空,或栈顶运算符为"(",则将此运算符入栈。
    4.2若该运算符优先级比栈顶运算符高,则将此运算符入栈。
    4.3否则,将s1栈顶运算符弹出并压入s2,回到4.1与新的栈顶运算符作比较。

  5. 遇到括号时,"(“直接压入s1,”)"则依次弹出s1中的运算符并压入s2,之后舍弃这对括号。

  6. 重复步骤2-5,直到表达式最右边。

  7. 将s1中剩余运算符压入s2.

  8. 依次弹出s2中的元素,其逆序即为后缀表达式。

2.注意:

(1)首次扫描到运算符时要么压入s1(该运算符优先级较高),要么将其他运算符弹出后压入s1(该运算符优先级较低),在扫描到优先级更低的运算符或全部扫描完成后压入s2,因此在运算的数之后。
(2)不考虑括号的情况下,"*/“会比"±"更先压入s2,且位置就在其操作数后面。
(3)括号中的数和操作符会在扫描到”)"时压入s2,比括号外更早压入,因此更早执行。

3.程序

​`class PolandCalculator {
public ArrayList arr;
public Stack s1 = new Stack<>(); // 保存符号的栈
public Stack s2 = new Stack<>(); // 保存中间变量的栈

public int priority(int operate) {  // 定义运算符优先级,*/大于+-
    int res = -1;
    switch (operate) {
        case '*', '/' -> res = 2;
        case '+', '-' -> res = 1;
        case '(' -> res = 0;
        default -> {}
    }
    return res;
}

public ArrayList<String> gen_expression(String expression) {  // 提取中缀表达式中的各项,并转为后缀表达式
    arr = new ArrayList<>();
    int index = -1;
    for (int i = 0; i < expression.length(); i++) {
        char ch = expression.charAt(i);
        if (ch >= 48 && ch <= 57) {
            if (index == -1) index = i;  // 前一位不是数字,这一位是数字,设置此数字的起始索引
        }
        else {
            if (index != -1) {
                arr.add(expression.substring(index, i));  // 数字结束,将其加入到ArrayList中
                index = -1;  // 此数字扫描结束,index设置成初值
            }
            if (ch == ' ') continue;;
            arr.add(ch + "");
        }
        if (i == expression.length() - 1) arr.add(expression.substring(index, i + 1));
    }
    // 将表达式转为后缀表达式
    for (String str : arr) {
        if (isDigit(str)) {
            s2.push(str);  // 若是数字则直接压入s2
        }
        else {  // 若是运算符
            char ch = str.charAt(0);
            if (s1.isEmpty() || ch == '(') s1.push(ch + "");  // s1为空或该符号为"("时直接压入s1
            else if (ch == ')') {  // 若是")"则将"("之后的所有运算符压入s2
                char last_operate = s1.pop().charAt(0);
                while (last_operate != '(') {
                    s2.push(last_operate + "");
                    last_operate = s1.pop().charAt(0);
                }
            }
            else {
                boolean isMin = false;  // 指示当前运算符是否优先级最小
                char last_operate = s1.pop().charAt(0);  // 否则弹出s1栈顶的运算符
                while (priority(ch) <= priority(last_operate)) {  // 当前运算符优先级小于等于栈顶运算符时将栈顶运算符压入s2,再和下一个栈顶运算符比较
                    s2.push(last_operate + "");
                    if (s1.isEmpty()) {
                        isMin = true;
                        break;
                    }
                    last_operate = s1.pop().charAt(0);
                }
                if (!isMin) s1.push(last_operate + "");
                s1.push(ch + "");
            }
        }
    }
    while (!s1.isEmpty()) s2.push(s1.pop());
    arr.clear();
    while (!s2.isEmpty()) arr.add(0, s2.pop());
    return arr;
}

public boolean isDigit(String sub) {
    boolean flag = true;
    for (int i = 0; i < sub.length(); i++) {
        char ch = sub.charAt(i);
        if (ch > 57 || ch < 48) {
            flag = false;
            break;
        }
    }
    return flag;
}

public int cal(int num1, int num2, int operate) {  // 定义将运算符和数pop后计算的过程,输入均为int,输出为计算结果
    int res = 0;
    switch (operate) {
        case '+' -> res = num2 + num1;
        case '-' -> res = num2 - num1;
        case '*' -> res = num2 * num1;
        case '/' -> res = num2 / num1;
        default -> {
        }
    }
    return res;
}

public int calculate(String expression) {
    Stack<Integer> stack = new Stack<>();
    ArrayList<String> suffix_expression = gen_expression(expression);
    for (String s : suffix_expression) {
        if (isDigit(s)) stack.push(Integer.parseInt(s));
        else {
            int num1 = stack.pop();
            int num2 = stack.pop();
            int res = cal(num1, num2, s.charAt(0));
            stack.push(res);
        }
    }
    return stack.pop();
}

}`

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
做一门精致,全面详细的 java数据结构与算法!!!让天下没有难学的数据结构,让天下没有难学的算法,不吹不黑,我们的讲师及其敬业,可以看到课程视频,课件,代码的录制撰写,都是在深夜,如此用心,其心可鉴,他不掉头发,谁掉头发???总之你知道的,不知道的,我们都讲,并且持续更新,走过路过,不要错过,不敢说是史上最全的课程,怕违反广告法,总而言之,言而总之,这门课你值得拥有,好吃不贵,对于你知识的渴求,我们管够管饱话不多说,牛不多吹,我们要讲的本门课程内容:稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀中缀后缀表达式中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值