简单四则运算解析器 | 豆包MarsCode AI 刷题

49 阅读4分钟

问题描述

小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。


测试样例

样例1:

输入:expression = "1+1"
输出:2

样例2:

输入:expression = "3+4*5/(3+2)"
输出:7

样例3:

输入:expression = "4+2*5-2/1"
输出:12

样例4:

输入:expression = "(1+(4+5+2)-3)+(6+8)"
输出:23

样例5:

输入:expression = "2*(5+5*2)/3+(6+8*3)"
输出:40

解题思路

计算器的实现也是一个很经典的问题了。相比于实现一个计算器,本题的要求显得比较简单,因为没有负数,没有空格,没有错误处理什么的,所以实现起来也比较简单,核心思路就是递归。理解起来的话就是一颗语法树,每一个叶子节点存放一个数,而父节点则存放运算符。而核心难点就是对运算符的优先级的处理,只要把这个解决了,那么这个问题剩下的就是按部就班的模拟了。 那么代码实现的话也是如此。

  • 首先,先定义运算符优先级,分别是:(,),*,/,+,-,如若优先级相同,则从左到右,但是实现的时候从右到左遍历的话会更简单。
  • 我的解决方式是,先处理 + 号,再处理 - 号,最后处理 * 和 /,在每一次处理中都对括号进行检查和删除(当为两端括号时),对括号的检查直接用栈的思想就行。
  • 注意题目中要求除法为整数除法,所以不留小数,这个容易被忽视。
  • 为什么从右到左进行遍历而不是从左到右呢?因为从左到右的话容易出现一种情况,那就是当一个子表达式前面有 - 号的时候,会产生意料之外的结果,如: -(1 + 2 + 3),按理说应该是-6,但在我的实现中不是(。而从右到左遍历的话子表达式会先计算出整体值后再与 - 号做计算,这样能减少代码量。 下面是golang的实现代码,时间复杂度的话取决于运算符的数量。
package main

import (
	"fmt"
	"strconv"
)

func eval(expression string) int {
	// 核心:以操作符优先级来划分
	bracketCount := 0
	length := len(expression)
	for i := length - 1; i >= 0; i-- {
		char := expression[i]
		switch char {
		case '(':
			bracketCount--
		case ')':
			bracketCount++
		default:
		}
		if bracketCount > 0 {
			continue
		}
		switch char {
		case '+':
			left := eval(expression[:i])
			right := eval(expression[i+1:])
			fmt.Println(left, "+", right, "=", left+right)
			return left + right
		default:
			continue
		}
	}
	for i := length - 1; i >= 0; i-- {
		char := expression[i]
		switch char {
		case '(':
			bracketCount--
		case ')':
			bracketCount++
		default:
		}
		if bracketCount > 0 {
			continue
		}
		switch char {
		case '-':
			left := eval(expression[:i])
			right := eval(expression[i+1:])
			fmt.Println(left, "-", right, "=", left-right)
			return left - right
		default:
			continue
		}
	}
	for i := length - 1; i >= 0; i-- {
		char := expression[i]
		switch char {
		case '(':
			bracketCount--
		case ')':
			bracketCount++
		default:
		}
		if bracketCount > 0 {
			continue
		}
		switch char {
		case '*':
			left := eval(expression[:i])
			right := eval(expression[i+1:])
			fmt.Println(left, "*", right, "=", left*right)
			return left * right
		case '/':
			left := eval(expression[:i])
			right := eval(expression[i+1:])
			fmt.Println(left, "/", right, "=", left/right)
			return left / right
		default:
			continue
		}
	}
	if expression[0] == '(' && expression[len(expression)-1] == ')' {
		return eval(expression[1 : len(expression)-1])
	}
	result, _ := strconv.ParseInt(expression, 10, 0)
	return int(result)
}

func solution(expression string) int {
	result := eval(expression)
	fmt.Println("res:", result)
	return result
}

func main() {
	// You can add more test cases here
	fmt.Println(solution("1+1") == 2)
	fmt.Println(solution("3+4*5/(3+2)") == 7)
	fmt.Println(solution("4+2*5-2/1") == 12)
	fmt.Println(solution("(1+(4+5+2)-3)+(6+8)") == 23)
	fmt.Println(solution("8-3+(6*4)/3") == 13)
	fmt.Println(solution("(6-2)+(5*3/5)") == 7)
	fmt.Println(solution("9/(3+2)-4") == -3)

}

下面是python的代码,由marscode转化而来,能通过提交。

def eval(expression):
    # 核心:以操作符优先级来划分
    bracket_count = 0
    length = len(expression)
    
    # 处理加法
    for i in range(length - 1, -1, -1):
        char = expression[i]
        if char == '(':
            bracket_count -= 1
        elif char == ')':
            bracket_count += 1
        if bracket_count > 0:
            continue
        if char == '+':
            left = eval(expression[:i])
            right = eval(expression[i+1:])
            print(f"{left} + {right} = {left + right}")
            return left + right
    
    # 处理减法
    for i in range(length - 1, -1, -1):
        char = expression[i]
        if char == '(':
            bracket_count -= 1
        elif char == ')':
            bracket_count += 1
        if bracket_count > 0:
            continue
        if char == '-':
            left = eval(expression[:i])
            right = eval(expression[i+1:])
            print(f"{left} - {right} = {left - right}")
            return left - right
    
    # 处理乘法和除法
    for i in range(length - 1, -1, -1):
        char = expression[i]
        if char == '(':
            bracket_count -= 1
        elif char == ')':
            bracket_count += 1
        if bracket_count > 0:
            continue
        if char == '*':
            left = eval(expression[:i])
            right = eval(expression[i+1:])
            print(f"{left} * {right} = {left * right}")
            return left * right
        elif char == '/':
            left = eval(expression[:i])
            right = eval(expression[i+1:])
            print(f"{left} / {right} = {left // right}")
            return left // right
    
    # 处理括号
    if expression[0] == '(' and expression[-1] == ')':
        return eval(expression[1:-1])
    
    # 处理数字
    return int(expression)

def solution(expression):
    result = eval(expression)
    print(f"res: {result}")
    return result

def main():
    # 你可以在这里添加更多的测试用例
    print(solution("1+1") == 2)
    print(solution("3+4*5/(3+2)") == 7)
    print(solution("4+2*5-2/1") == 12)
    print(solution("(1+(4+5+2)-3)+(6+8)") == 23)
    print(solution("8-3+(6*4)/3") == 13)
    print(solution("(6-2)+(5*3/5)") == 7)
    print(solution("9/(3+2)-4") == -3)

if __name__ == "__main__":
    main()

总结

这是一道很经典的题目,做起来的不难,只要点通运算符的顺序以及从右到左遍历之后,那就不成问题了。