题解:AcWing 3302 表达式求值

【题目来源】

AcWing:3302 表达式求值 - AcWing题库

【题目描述】

给定一个表达式,其中运算符仅包含 +,-,*,/(加 减 乘 整除),可能包含括号,请你求出表达式的最终值。

【输入】

共一行,为给定表达式。

【输出】

共一行,为表达式的结果。

【输入样例】

(2+2)*(1+1)

【输出样例】

8

【核心思想】

  1. 问题分析:给定一个包含 +-*/ 运算符和括号的中缀表达式,需要计算其最终值。这是一个双栈表达式求值问题,关键在于正确处理运算符优先级和括号嵌套。

  2. 算法选择

    • 双栈法(Two Stacks):使用操作数栈 num 存储数字,运算符栈 op 存储运算符和括号
    • 运算符优先级:定义优先级映射表 pr = {'+':1, '-':1, '*':2, '/':2},乘除优先级高于加减
    • 栈顶计算(eval):遇到高优先级运算时,立即弹出栈顶两个操作数和运算符进行计算
  3. 关键步骤

    • 初始化:创建空的操作数栈 num 和运算符栈 op,定义运算符优先级表
    • 遍历表达式(对于每个字符 \(c\)):
      • 数字处理:连续读取数字字符,构建完整数字后压入 num
      • 左括号 (:直接压入 op
      • 右括号 ):循环执行 eval() 直到遇到左括号,然后弹出左括号
      • 运算符+-*/):
        • op 栈非空、栈顶不是 (、且栈顶优先级 \(\geq\) 当前运算符时,执行 eval()
        • 将当前运算符压入 op
    • 清空运算符栈:遍历结束后,执行剩余的所有 eval() 操作
    • 输出结果num 栈顶即为表达式结果
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),每个字符处理一次,每个运算符最多入栈出栈一次
    • 空间复杂度:\(O(n)\),两个栈的空间,最坏情况下存储所有字符
  5. 双栈表达式求值的核心思想

    • 延迟计算:低优先级运算符先入栈等待,遇到高优先级或右括号时再计算
    • 优先级控制:通过比较栈顶与当前运算符优先级,确保先计算高优先级运算
    • 括号处理:左括号作为屏障,右括号触发括号内所有运算的结算
    • 栈顶计算函数:统一封装计算逻辑,保证操作数顺序正确(先弹出的是右操作数)
    • 适用于中缀表达式求值、编译器语法分析、计算器实现等场景

【解题思路】

【解题思路2】

【算法标签】

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// 定义两个栈:操作数栈和运算符栈
stack<int> num;  // 存储操作数的栈(整数类型)
stack<char> op;  // 存储运算符的栈(字符类型)

/**
 * 执行栈顶运算:从栈中弹出两个操作数和一个运算符,计算结果后压回栈中
 * 注意:先弹出的是第二个操作数,后弹出的是第一个操作数
 */
void eval()
{
    // 从操作数栈中弹出第二个操作数(栈顶元素)
    auto b = num.top();
    num.pop();

    // 从操作数栈中弹出第一个操作数
    auto a = num.top();
    num.pop();

    // 从运算符栈中弹出运算符
    auto c = op.top();
    op.pop();

    int x;  // 存储运算结果

    // 根据运算符执行相应的算术运算
    if (c == '+')
    {
        x = a + b;  // 加法运算
    }
    else if (c == '-')
    {
        x = a - b;  // 减法运算
    }
    else if (c == '*')
    {
        x = a * b;  // 乘法运算
    }
    else
    {
        x = a / b;  // 除法运算(整数除法)
    }

    // 将计算结果压回操作数栈
    num.push(x);
}

int main()
{
    // 定义运算符优先级映射表
    // 优先级数值越大,优先级越高
    unordered_map<char, int> pr = {
        {'+', 1},  // 加法优先级为1
        {'-', 1},  // 减法优先级为1
        {'*', 2},  // 乘法优先级为2
        {'/', 2}   // 除法优先级为2
    };

    string str;  // 存储输入的中缀表达式字符串
    cin >> str;  // 读取表达式

    // 遍历表达式中的每个字符
    for (int i = 0; i < str.size(); i++)
    {
        auto c = str[i];  // 当前字符

        // 处理数字字符
        if (isdigit(c))
        {
            int x = 0;    // 当前数字的值
            int j = i;    // 用于扫描连续数字的指针

            // 提取连续的数字字符,处理多位数
            while (j < str.size() && isdigit(str[j]))
            {
                // 将字符转换为数字并累加
                x = x * 10 + (str[j] - '0');
                j++;
            }

            i = j - 1;    // 更新主循环索引,跳过已处理的数字
            num.push(x);  // 将数字压入操作数栈
        }
        // 处理左括号:直接压入运算符栈
        else if (c == '(')
        {
            op.push(c);
        }
        // 处理右括号:计算括号内的所有表达式
        else if (c == ')')
        {
            // 不断计算直到遇到左括号
            while (op.top() != '(')
            {
                eval();  // 执行计算
            }
            op.pop();  // 弹出左括号
        }
        // 处理运算符:+ - * /
        else
        {
            // 当栈不为空,且栈顶不是左括号,且栈顶运算符优先级不低于当前运算符时
            while (op.size() > 0 && op.top() != '(' && pr[op.top()] >= pr[c])
            {
                eval();  // 先计算优先级高的运算
            }
            op.push(c);  // 当前运算符压栈
        }
    }

    // 处理运算符栈中剩余的所有运算
    while (op.size() > 0)
    {
        eval();
    }

    // 输出最终结果(操作数栈中唯一的元素)
    cout << num.top() << endl;

    return 0;
}
#include <bits/stdc++.h>
using namespace std;

stack<int> num;  // 操作数栈,存储数字
stack<char> op;  // 操作符栈,存储运算符和括号
string str;      // 输入的中缀表达式字符串

// 运算符优先级映射表
map<char, int> pr = {
    {'+', 1}, {'-', 1},  // 加减法优先级1
    {'*', 2}, {'/', 2},  // 乘除法优先级2
    {'^', 3}             // 乘方优先级3(最高)
};

int top = 0;  // 栈顶指针(实际未使用,因为使用STL stack)

/**
 * 执行计算操作:从栈中弹出两个操作数和一个运算符,计算结果压回栈中
 */
void eval()
{
    // 弹出第二个操作数(栈顶)
    auto b = num.top();
    num.pop();

    // 弹出第一个操作数
    auto a = num.top();
    num.pop();

    // 弹出运算符
    auto c = op.top();
    op.pop();

    // 根据运算符执行相应的计算
    switch (c)
    {
        case '+':
            num.push(a + b);  // 加法
            break;
        case '-':
            num.push(a - b);  // 减法
            break;
        case '*':
            num.push(a * b);  // 乘法
            break;
        case '/':
            num.push(a / b);  // 除法
            break;
        // 注意:代码中定义了^的优先级,但没有实现^的计算
    }
}

int main()
{
    // 读入中缀表达式
    cin >> str;

    // 遍历表达式的每个字符
    for (int i = 0; i < str.size(); i++)
    {
        char c = str[i];

        if (c >= '0' && c <= '9')  // 处理数字
        {
            int d = 0;  // 当前数字的值
            int j = i;  // 扫描指针

            // 提取连续的数字字符,处理多位数
            while (j < str.size() && str[j] >= '0' && str[j] <= '9')
            {
                d = d * 10 + (str[j] - '0');  // 逐位构建数字
                j++;
            }

            // 将数字压入操作数栈
            num.push(d);

            // 更新索引(跳过已处理的数字字符)
            i = j - 1;
        }
        else if (c == '(')  // 左括号:直接入栈
        {
            op.push(c);
        }
        else if (c == ')')  // 右括号:计算括号内的表达式
        {
            // 弹出并计算直到遇到左括号
            while (op.top() != '(')
            {
                eval();
            }
            op.pop();  // 弹出左括号
        }
        else  // 处理运算符:+ - * / ^
        {
            // 弹出并计算优先级高于或等于当前运算符的运算符
            while (op.size() > 0 && op.top() != '(' && pr[op.top()] >= pr[c])
            {
                eval();
            }
            op.push(c);  // 当前运算符入栈
        }
    }

    // 处理栈中剩余的运算符
    while (op.size() > 0)
    {
        eval();
    }

    // 输出最终结果(栈中唯一的数字)
    cout << num.top() << endl;

    return 0;
}

【运行结果】

(2+2)*(1+1)
8
posted @ 2026-06-14 10:36  团爸讲算法  阅读(11)  评论(0)    收藏  举报