栈,中缀表达式转后缀表达式及逆波兰计算器的实现【Java实现】

一、中缀表达式转后缀表达式(逆波兰表达式)

1、思路

1. 初始化两个栈:运算符栈 s1 和存储中间结果的栈 s2;
2. 从左至右扫描中缀表达式;
3. 遇到操作数时,将其压入 s2;
4. 遇到运算符时,比较其与 s1 栈顶运算符的优先级:
	1. 如果 s1 为空,或者 s1 栈顶元素为左括号 **“(”**,则直接压入s1;
	2. 如果其优先级比栈顶运算符的优先级高,也将其压入 s1;
	3. 如果上述两个条件都不满足,则将 s1 栈顶的元素弹出并压入 s2,再重复	前两步。
5. 遇到括号时:
	1. 如果是左括号 **“(”**,则直接压入 s1;
	2. 如果是右括号 **“)”**,则依次弹出 s1 栈顶的元素,并压入 s2,直到遇到左括号 **“(”** 为止,此时将这一对括号丢弃(弹栈)。
6. 重复步骤 2~5,直到表达式的最右端;
7. 将 s1 中剩余的元素依次弹出并压入 s2;
8. 依次弹出 s2 中的元素并输出,`结果的逆序即为当前中缀表达式对应的后缀表达式`

2. 示例

将中缀表达式 1 + ( ( 2 + 3 ) ) * 4 ) - 5 按照上述步骤转换为后缀表达式

在这里插入图片描述

3. 代码实现

1. 将中缀表达式存储在List集合中

代码如下:

/**
 * @param expr 中缀表达式字符串
 * @return 存储着中缀表达式截取元素的List
 */
public static List<String> toList(String expr) {
    //初始化list
    List<String> list = new ArrayList<>();
    //扫描索引
    int index = 0;
    //用以存放每次扫描的元素
    String s = "";
    //用来拼接可能有的多位数
    String ex = "";
    //只要索引小于表达式长度,就一直扫描
    while (index < expr.length()) {
        //用s存放当前扫描到的元素,每扫描一个元素,索引index+1
        s = expr.substring(index, ++index);
        //正则判断当前元素是否为数字
        if (s.matches("\\d")) {
            //如果是数字,则拼接当前数字
            ex += s;
            //如果索引等于表达式长度,则表示已经扫描到表达式末尾,直接将当前数字添加进list
            if (index == expr.length()) {
                list.add(s);
            } else {
                //如果没有扫描到表达式末尾,则判断当前元素的下一个是否为数字
                //如果不是数字,则直接将当前数字添加进list,并重置多位数拼接字符串ex
                if (!expr.substring(index, index + 1).matches("\\d")) {
                    list.add(ex);
                    ex = "";
                }
            }
        } else {
            //如果是字符,则直接添加进list
            list.add(s);
        }
    }
    //返回存储表达式截取元素的list
    return list;
}

/**
 * 测试截取方法是否正确
 * 测试结果:
 * 当前中缀表达式为:1+((2+3)*4)-5
 * 存储截取元素的list:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
 */
public static void main(String[] args) {
        String expr = "1+((2+3)*4)-5";
        System.out.println("当前中缀表达式为:" + expr);
        List<String> list = toList(expr);
        System.out.print("存储截取元素的list:");
        System.out.println(list);
}

2. 判断运算符优先级

代码如下:

/**
 * 判断运算符的优先级
 * @param oper 运算符字符串
 * @return 运算符的优先级(数值越大,优先级越高)
 */
public static int priority(String oper) {
    if ("+".equals(oper) || "-".equals(oper)) {
        return 0;
    } else if ("*".equals(oper) || "/".equals(oper)) {
        return 1;
    } else {
        return -1;
    }
}

3. 将中缀表达式转为后缀表达式

代码如下:

/**
 * @param list 存储着中缀表达式截取元素的List
 * @return 当前中缀表达式转换的后缀表达式
 */
public static List<String> mid2Back(List<String> list) {
    //初始化符号栈s1
    Stack<String> s1 = new Stack<>();
    //初始化中间结果栈s2
    //因为s2在整个过程中没有出栈操作,且后面还需要元素逆序输出,所以此处采用List简化操作
    List<String> s2 = new ArrayList<>();
    //遍历list中的元素
    for (String s : list) {
        if (s.matches("\\d+")) {
            //如果当前元素是数字(正则匹配多位数),则直接添加进s2
            s2.add(s);
        } else if ("(".equals(s)) {
            //如果当前元素为左括号 ( ,则直接压入s1
            s1.push(s);
        } else if (")".equals(s)) {
            //如果当前元素为右括号 ) ,则查看栈顶元素是否为左括号 (
            while (!"(".equals(s1.peek())) {
                //将s1栈顶元素出栈,并添加进s2,一直到s1栈顶元素为左括号 ( 为止
                s2.add(s1.pop());
            }
            //将左括号 ( 出栈
            s1.pop();
        } else {
            //如果当前元素为运算符
            //判断s1是否为空且当前运算符的优先级是否高于s1栈顶元素的优先级
            while (s1.size() != 0 && priority(s) <= priority(s1.peek())) {
                //如果s1不为空且当前运算符的优先级不高于s1栈顶元素的优先级
                //则将s1栈顶元素出栈,且添加进s2
                s2.add(s1.pop());
            }
            //将当前运算符压入s1
            s1.push(s);
        }
    }
    //将s1中剩余元素出栈并添加进s2
    while (s1.size() != 0) {
        s2.add(s1.pop());
    }
    //返回存储后缀表达式的List(因为List是有序的,所以此处不用再逆序)
    return s2;
}

/**
 * 测试前面的所有方法是否正确
 * 测试结果:
 * 当前中缀表达式为:1+((2+3)*4)-5
 * 存储截取元素的list:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
 * 当前中缀表达式对应的后缀表达式为:[1, 2, 3, +, 4, *, +, 5, -]
 */
public static void main(String[] args) {
    String expr = "1+((2+3)*4)-5";
    System.out.println("当前中缀表达式为:" + expr);
    List<String> list = toList(expr);
    System.out.print("存储截取元素的list:");
    System.out.println(list);
    System.out.print("当前中缀表达式对应的后缀表达式为:");
    List<String> list1 = mid2Back(list);
    System.out.println(list1);
}

二、逆波兰计算器

能够支持多位整数进行四则运算的简易计算器

1. 思路

1. 从左至右扫描后缀表达式
2. 如果是数字,则直接压入栈
3. 如果是运算符,则弹出栈顶的两个元素与当前运算符进行运算(后弹出的元素对先弹出的元素进行运算),并将结果入栈
4. 重复上述过程直到扫描到表达式的最右端
5. 栈中最后剩下的元素即当前后缀表达式的运算结果

2. 示例

将后缀表达式 1 2 3 + 4 * + 5 - 按照上述步骤转换为后缀表达式

在这里插入图片描述

3. 代码实现

代码如下:

/**
 * @param list 后缀表达式截取元素的list
 * @return 表达式运算结果
 */
public static int calc(List<String> list) {
    //初始化存放数据的栈
    Stack<String> stack = new Stack<>();
    //遍历list
    for (String s : list) {
        if (s.matches("\\d+")) {
            //如果当前元素为数字(正则匹配多位数),则直接入栈
            stack.push(s);
        } else {
            //如果当前元素为运算符
            //则从栈中依次弹出两个元素
            int num2 = Integer.parseInt(stack.pop());
            int num1 = Integer.parseInt(stack.pop());
            //用后弹出的元素(num1)对先弹出的元素(num2)进行四则运算
            //再将运算结果压入栈
            switch (s) {
                case "+":
                    stack.push((num1 + num2) + "");
                    break;
                case "-":
                    stack.push((num1 - num2) + "");
                    break;
                case "*":
                    stack.push((num1 * num2) + "");
                    break;
                case "/":
                    stack.push((num1 / num2) + "");
                    break;
                default:
                    throw new RuntimeException("运算符不匹配!");
            }
        }
    }
    //最后在栈中只剩下一个元素,即为表达式的运算结果
    return Integer.parseInt(stack.pop());
}

/**
 * 测试前面的所有方法是否正确
 * 测试结果:
 * 当前中缀表达式为:1+((2+3)*4)-5
 * 存储截取元素的list:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
 * 当前中缀表达式对应的后缀表达式为:[1, 2, 3, +, 4, *, +, 5, -]
 * 1+((2+3)*4)-5 = 16
 */
public static void main(String[] args) {
    String expr = "1+((2+3)*4)-5";
    System.out.println("当前中缀表达式为:" + expr);
    List<String> list = toList(expr);
    System.out.print("存储截取元素的list:");
    System.out.println(list);
    System.out.print("当前中缀表达式对应的后缀表达式为:");
    List<String> list1 = mid2Back(list);
    System.out.println(list1);
    int result = calc(list1);
    System.out.println(expr+" = "+result);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值