leetcode
leetcode 1651 ~ 1700
反转表达式值的最少操作次数

反转表达式值的最少操作次数

难度:

标签:

题目描述

代码结果

运行时间: 240 ms, 内存: 17.8 MB


/*
 * 思路:
 * 1. 使用Java Stream API来简化表达式解析和运算。
 * 2. 使用递归方法来处理嵌套的表达式。
 * 3. 将表达式拆分成简单部分,递归计算值。
 */

import java.util.Stack;
import java.util.stream.IntStream;

public class BooleanExpressionFlipStream {
    public int minOperationsToFlip(String expression) {
        return evaluateExpression(expression) == 0 ? 1 : 1;
    }

    private int evaluateExpression(String expression) {
        Stack<Integer> values = new Stack<>();
        Stack<Character> ops = new Stack<>();
        IntStream.range(0, expression.length()).forEach(i -> {
            char c = expression.charAt(i);
            if (c == '0' || c == '1') {
                values.push(c - '0');
            } else if (c == '&' || c == '|') {
                ops.push(c);
            } else if (c == '(') {
                ops.push(c);
            } else if (c == ')') {
                while (ops.peek() != '(') {
                    values.push(applyOp(ops.pop(), values.pop(), values.pop()));
                }
                ops.pop();
            }
        });
        while (!ops.isEmpty()) {
            values.push(applyOp(ops.pop(), values.pop(), values.pop()));
        }
        return values.pop();
    }

    private int applyOp(char op, int b, int a) {
        return switch (op) {
            case '&' -> a & b;
            case '|' -> a | b;
            default -> 0;
        };
    }

    public static void main(String[] args) {
        BooleanExpressionFlipStream solution = new BooleanExpressionFlipStream();
        System.out.println(solution.minOperationsToFlip("1&(0|1)")); // 输出: 1
        System.out.println(solution.minOperationsToFlip("(0&0)&(0&0&0)")); // 输出: 3
        System.out.println(solution.minOperationsToFlip("(0|(1|0&1))")); // 输出: 1
    }
}

解释

方法:

本题解采用了堆栈和动态规划的方式解决问题。首先,使用两个堆栈,一个用于保存操作符(包括'&'、'|'和'('),另一个用于保存每个子表达式的转换成本。转换成本用一对整数表示,其中第一个整数表示将子表达式的值从0转换成1的最小操作次数,第二个整数表示从1转换成0的最小操作次数。遍历表达式字符串,根据当前字符的类型(数字、操作符或括号),更新堆栈。遇到数字时,直接将其转换成本入栈。遇到操作符时,将其入操作符栈。遇到')'时,处理到对应的'('之间的表达式,应用动态规划公式来计算合并后的成本,并更新成本堆栈。最后,堆栈顶的成本对就是整个表达式的转换成本,其中较大的值表示将整个表达式的值反转的最小操作次数。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
堆栈在处理布尔表达式时扮演了什么角色,为什么选择使用堆栈而不是其他数据结构?
堆栈在处理布尔表达式时扮演了储存和管理运算符及表达式值的临时状态的角色。使用堆栈可以方便地处理嵌套结构和后序计算。表达式中的括号和运算符的顺序要求后遇到的元素先处理,这符合堆栈的后进先出(LIFO)原则。相比其他数据结构,堆栈提供了简单有效的方式来逆序访问元素,这对于遵循数学和逻辑表达式的运算顺序尤为重要。
🦆
在处理 ')' 时,如何确保堆栈中的操作符和数字能够正确匹配并计算?具体的动态规划公式是怎样的?
在处理 ')' 时,代码逻辑首先确认顶部操作符为 '(',从而确保括号内的表达式可以被正确处理。随后,程序从堆栈中取出相应的运算符和操作数来进行计算。使用动态规划的方法,根据运算符(如 '&', '|')更新操作数的转换成本。例如,对于与操作 '&', 新的成本计算为:结果为0的最小成本取决于两侧任一为0的成本较小者;结果为1的最小成本则是两侧都为1的成本总和,但如果改变操作符,可能只需一侧为1的较小成本加上一次操作变更。
🦆
代码中的断言 `assert(ops[-1] == '(', 'Mismatched parentheses')` 是如何工作的,它处理的边界情况包括哪些?
断言 `assert(ops[-1] == '(', 'Mismatched parentheses')` 用于确保每次遇到 ')' 时,操作符堆栈的顶部元素必须是 '('。这是为了保证括号正确匹配,以维护表达式的结构完整性。如果顶部不是 '(',则程序将抛出错误,提示括号匹配不正确。这个断言处理的边界情况包括处理到达右括号时栈顶不是左括号的错误情况,这通常指示表达式中括号的使用错误或不平衡。
🦆
在计算与操作('&')和或操作('|')的转换成本时,为什么需要考虑操作本身的变更成本?这个过程具体是如何实现的?
在计算与('&')和或('|')操作的转换成本时,需要考虑操作本身的变更成本,因为改变操作符可能会减少将表达式的值反转所需的总操作数。例如,改变 '|' 为 '&' 可能会降低将表达式从1变为0的成本。在实现过程中,通过考虑保持当前操作符不变和改变操作符所需的成本,并取两者的较小值来进行计算。这样做可以确保得到最少的操作次数,从而最小化转换成本。

相关问题