You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求布尔代数复杂表达式转逆波兰表示法(RPN)的实现方案

布尔代数表达式转后缀形式并求值(支持复杂表达式)

你的现有代码仅能处理双操作数加单个二元运算符的简单表达式,要支持带否定(~)、蕴含(→)的复杂表达式,标准解决方案是使用Shunting-yard算法(调度场算法),它能妥善处理运算符优先级、一元运算符与括号,完全适配你的需求。

核心实现逻辑

1. 明确运算符规则

先定义布尔运算符的优先级(从高到低)与结合性:

  • ~(否定,一元):优先级最高,右结合(如~~X等价于~(~X))
  • ^(合取):优先级次之,左结合
  • v(析取):优先级再次之,左结合
  • →(蕴含):优先级最低,左结合(逻辑上P→Q等价于~PvQ,转后缀时直接按运算符处理即可)

2. 表达式分词(Tokenize)

将输入的表达式字符串拆分为单个token(操作数、运算符、括号),比如~X v Y → Y会被拆分为['~', 'X', 'v', 'Y', '→', 'Y'],处理时自动忽略空格。

3. 调度场算法转后缀表达式

遍历每个token,按以下逻辑处理:

  • 若为操作数(X/Y等变量),直接加入输出队列
  • 若为运算符:
    • 对于一元运算符~,先判断其是否为一元(出现在表达式开头、运算符后或左括号后),再将栈中优先级更高的运算符弹出至输出队列,最后压入当前运算符
    • 对于二元运算符,将栈中优先级更高或相等(左结合)的运算符弹出至输出队列,再压入当前运算符
  • 若为左括号,直接压入栈;若为右括号,弹出栈中运算符至输出队列,直到遇到左括号,弹出左括号但不加入输出
  • 遍历结束后,将栈中剩余运算符全部弹出至输出队列

4. 后缀表达式求值

遍历后缀表达式,用栈处理:

  • 遇到操作数,将其对应的布尔值(用户设置的1/0)压入栈
  • 遇到运算符,从栈中弹出对应数量的操作数(一元弹1个,二元弹2个),计算结果后压回栈
  • 最终栈顶值即为表达式结果

完整实现代码

// 用户设置的变量值,可直接修改
const vars = {
  X: 1,
  Y: 0
};

// 运算符配置:优先级、是否为一元、结合性
const operators = {
  '~': { precedence: 4, isUnary: true, associativity: 'right' },
  '^': { precedence: 3, isUnary: false, associativity: 'left' },
  'v': { precedence: 2, isUnary: false, associativity: 'left' },
  '→': { precedence: 1, isUnary: false, associativity: 'left' }
};

// 表达式分词函数
function tokenize(expression) {
  const tokens = [];
  let i = 0;
  const len = expression.length;
  while (i < len) {
    const char = expression[i];
    // 跳过空格
    if (char.trim() === '') {
      i++;
      continue;
    }
    // 匹配运算符或括号
    if (operators[char] || char === '(' || char === ')') {
      tokens.push(char);
      i++;
    } else {
      // 匹配单个字母变量,如需多字符变量可扩展此处逻辑
      tokens.push(char);
      i++;
    }
  }
  return tokens;
}

// 中缀表达式转后缀表达式
function infixToPostfix(tokens) {
  const output = [];
  const stack = [];
  
  for (let i = 0; i < tokens.length; i++) {
    const token = tokens[i];
    
    // 操作数直接加入输出
    if (!operators[token] && token !== '(' && token !== ')') {
      output.push(token);
      continue;
    }
    
    // 左括号压栈
    if (token === '(') {
      stack.push(token);
      continue;
    }
    
    // 右括号弹出至左括号
    if (token === ')') {
      while (stack.length > 0 && stack[stack.length - 1] !== '(') {
        output.push(stack.pop());
      }
      stack.pop(); // 弹出左括号,不加入输出
      continue;
    }
    
    // 处理运算符
    const currentOp = operators[token];
    // 判断~是否为一元运算符
    const isUnary = currentOp.isUnary && (
      i === 0 || 
      operators[tokens[i-1]] || 
      tokens[i-1] === '('
    );
    
    while (stack.length > 0) {
      const topToken = stack[stack.length - 1];
      if (topToken === '(') break;
      
      const topOp = operators[topToken];
      // 比较优先级,决定是否弹出栈顶运算符
      const shouldPop = isUnary 
        ? (topOp.precedence > currentOp.precedence)
        : (currentOp.associativity === 'left' && topOp.precedence >= currentOp.precedence) ||
          (currentOp.associativity === 'right' && topOp.precedence > currentOp.precedence);
      
      if (shouldPop) {
        output.push(stack.pop());
      } else {
        break;
      }
    }
    stack.push(token);
  }
  
  // 弹出栈中剩余运算符
  while (stack.length > 0) {
    output.push(stack.pop());
  }
  
  return output.join('');
}

// 计算后缀表达式结果
function evaluatePostfix(postfix) {
  const stack = [];
  for (const char of postfix) {
    if (!operators[char]) {
      // 压入变量对应的值
      stack.push(vars[char]);
    } else {
      const op = operators[char];
      if (op.isUnary) {
        // 一元运算符取反
        const val = stack.pop();
        stack.push(val === 0 ? 1 : 0);
      } else {
        // 二元运算符:注意弹出顺序,先弹的是右操作数
        const right = stack.pop();
        const left = stack.pop();
        let result;
        switch (char) {
          case '^':
            result = left & right; // 合取:同1为1
            break;
          case 'v':
            result = left | right; // 析取:有1为1
            break;
          case '→':
            result = left === 1 && right === 0 ? 0 : 1; // 蕴含:仅1→0为0,其余为1
            break;
        }
        stack.push(result);
      }
    }
  }
  return stack.pop();
}

// 测试示例
const inputExpr = "~X v Y → Y";
const tokens = tokenize(inputExpr);
const postfix = infixToPostfix(tokens);
const result = evaluatePostfix(postfix);

console.log(`原表达式:${inputExpr}`);
console.log(`后缀形式:${postfix}`);
console.log(`计算结果:${result}`);

代码说明

  • 变量vars用于存储用户设置的X/Y等值,可直接修改
  • 支持带括号的复杂表达式,比如~(X v Y) ^ Z
  • 蕴含运算符→的计算逻辑严格遵循布尔代数规则,也可替换为转成~PvQ后再计算
  • 分词函数默认支持单个字母变量,如需多字符变量可扩展分词逻辑

内容的提问来源于stack exchange,提问作者user526218

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 02:50:26