LeetCode 241. Different Ways to Add Parentheses

@(LeetCode)

问题描述

给定一个由数字和运算符组成的字符串,返回数字与运算符所有可能的组合方式的计算结果。有效的操作符为 +-*

栗 1:

输入:"2-1-1"
输出:[0, 2]

可能的组合方式如下:

((2-1)-1) = 0 
(2-(1-1)) = 2

栗 2:

输入:"2*3-4*5"
输出:[-34, -14, -10, -10, 10]

可能的组合方式如下:

(2*(3-(4*5))) = -34 
((2*3)-(4*5)) = -14 
((2*(3-4))*5) = -10 
(2*((3-4)*5)) = -10 
(((2*3)-4)*5) = 10

想看英文原文的戳这里

解题思路

这里的题意是忽略运算符优先级,找出所有可能的数字与运算符的结合方式,再计算其结果。

栗子分析

我的思路是由分析栗 2 得出的,让我们再来整理一下栗 2 的结果。

表达式为:"2*3-4*5"

所有可能的组合方式如下,我稍微整理了下。

// 第一组组合。由第一个操作符 '*' 连接左右两边的组合
2*(3-(4*5)) = -34
2*((3-4)*5) = -10

// 第二组组合。由第二个操作符 '-' 连接左右两边的组合
(2*3)-(4*5) = -14

// 第三组组合。由第三个操作符 '*' 连接左右两边的组合
(2*(3-4))*5 = -10
((2*3)-4)*5 = 10

从上,我们可以发现一些规律。

第一组组合由 '*' 分隔表达式;第二组组合由 '-' 分隔;第三组组合由 '*' 分隔,且从整体上来看将表达式分成了左右两部分。再细看左右两部分,不难发现:

  • 如果左/右两部分是表达式,则继续按照上述方式进行分解组合。
  • 如果左/右两部分是数字,则不再分解。

比如:2*(3-4*5),其右边部分 (3-4*5) 是个表达式,又可分解组合如下:

3-(4*5)
(3-4)*5

而左边 2 是数字,不可再分解。因此,2*(3-4*5) 所有的组合方式为:

2*(3-(4*5))
2*((3-4)*5)

再来看个复杂些的栗子。

比如某个表达式为:(2-1*3)*(3-4*5),中间的 '*' 将表达式分为 (2-1*3)(3-4*5)

其中左边表达式 (2-1*3) 的组合方式如下:

2-(1*3) = -1
(2-1)*3 = 3

右边表达式 (3-4*5) 的组合方式如下:

3-(4*5) = -17
(3-4)*5 = -5

那么该表达式的所有组合方式有 2 * 2 = 4 种,为左右组合方式的两两组合。如下所示:

(2-(1*3))*(3-(4*5)) = -1 * -17 = 17
((2-1)*3)*(3-(4*5)) = 3 * -17 = -51

(2-(1*3))*((3-4)*5) = -1 * -5 = 5
((2-1)*3)*((3-4)*5) = 3 * -5 = -15

因此,以上的结论可表述为:表达式的组合方式 =「左边表达式可能的组合」* 「右边表达式可能的组合」,即再次两两组合。

弄清楚如何进行组合,那么下一步就是计算组合的运算结果。

何时能计算结果?当分解到左右两边都为数字时,这时候就需要计算结果了。根据中间操作符,左右两边的数字即可运算得出。但是要注意一点,由于组合方式有多种,那么组合计算的结果也有多个,需要用数组保存。

对于上述栗子 (2-1*3)*(3-4*5),其所有组合的值为:

// 两个数组元素两两相乘
[-1, 3] * [-17, -5] = [17, 5, -51, -15]

所以,我们也能得出如下结论:表达式所有组合的值 = 「左边表达式组合的值」op「右边表达式组合的值」。其中 op 代表操作符。

思路总结

根据以上栗子的讲解,我们总结一下思路:

  • 首先将整个表达式看成只有两部分组成,由一个操作符隔开。
  • 遍历表达式中所有的操作符,根据不同位置的操作符将其划分为两部分。
  • 选定一个操作符,该操作符会将表达式分为左右两部分。如果左/右部分也是一个表达式,则继续按上述方式分解。最终的组合为左右两部分结果的再次两两组合。
  • 由于子表达式会有多个组合,所以需要用数组保存所有组合方式运算后的值。为了一致性处理,即使只是单个值,也要以数组形式返回。
  • 表达式所有组合的值为「左表达式所有组合的值」与「右表达式所有组合的值」两两运算。

具体实现

理清思路后,有没有暗暗感觉到这是一道递归算法题,因为子表达式也以相同方式进行分解组合。

实现就比较简单了,但是要注意几个地方。

  1. 表达式分解后,对数字的判断。因为这里我犯了个低级的错误,受题目栗子影响,误认为当表达式长度为 1 时就是数字。(⊙o⊙)。
  2. 考虑当表达式不存在操作符时的边界条件。这时不能将表达式分为两部分,要注意这里的处理。
  3. 可将表达式计算的结果缓存,防止重复计算。

核心代码如下:

// start 代表字符串左边界,end 代表字符串右边界
var recursive2 = function (input, start, end) {
    if (start < 0 || end > input.length || start > end) 
    {
        return []
    }

    // 取出子表达式
    const substr = input.slice(start, end + 1)

    // 如果有缓存
    if (map.has(substr)) {
        return map.get(substr)
    }

    let resultList = []
    let i = start
    while (i <= end) {
        const op = input[i]
        if (op === '+' || op === '-' || op === '*') {
            // 分别计算左/右两边的值
            const leftPartResult = recursive(input, start, i - 1)
            const rightPartResult = recursive(input, i + 1, end)

            // 运算符函数
            const opFunc = opMap[op]

            // 两个数组做运算
            leftPartResult.forEach(left => {
                rightPartResult.forEach(right => {
                    const result = opFunc(left, right)
                    resultList.push(result)
                })
            });
        }

        i += 1
    }

    // 说明是数字
    if (resultList.length === 0) {
        // 以 [num] 方式返回
        resultList.push(parseInt(substr))
    }
    
    // 缓存
    map.set(substr, resultList)

    return resultList
}

完整代码可点此查看

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342