[字符串 分治法] 241. 为运算表达式设计优先级 (分治法)
241. 为运算表达式设计优先级
题目链接:https://leetcode-cn.com/problems/different-ways-to-add-parentheses/
分类:
- 字符串:解析字符串上的数字,运算符、构造所有可能的括号组合;
- 分治法:按运算符划分式子,返回子式的计算结果集合;
- 动态规划:见参考链接的解法2.
思路:分治法 (递归实现)
问题分解:
例如:2*3-4*5
- 整个式子按第一个乘号可以划分成 2 和 3 - 4 * 5,
- 第二个式子可以继续划分成:3和4 * 5 或 3 - 4和5,
- 而4 * 5可以继续划分为4和5;3-4也可以继续划分为3和4.
这样可以将计算整个式子不断分割,大问题分解为小问题,划分过程可以用递归实现。
递归函数的返回值: 返回的是结果集合,例如3 - 4 * 5会返回(3-4) * 5 = -5和3 - (4 * 5) = -17。我们使用一个List<Integer> 来存放一个结果集合。
递归出口: 如果式子划分到只包含数字,则直接返回数字。如果式子不止有数字,就进入递归主体继续划分。
递归主体:遍历式子的每个字符,找出式子中的运算符,按该运算符将式子划分为两部分,这两部分继续进入下一层递归。
- 式子的划分可以用substring来实现。
然后获取这两部分递归的返回值,得到的是划分的两个子式各自的结果集合,在这两个返回值集合里各选取一个元素,用当前运算符进行组合,得到的结果存入当前递归层维护的结果集合res中。
所有元素处理完毕后,返回当前递归层的结果集合res。
实现代码:
class Solution {
public List<Integer> diffWaysToCompute(String input) {
//特殊用例:input为null或为""
if(input == null || input.length() == 0) return new ArrayList<Integer>();
List<Integer> res = new ArrayList<Integer>();
//提取当前字符串的第一个数字
int index = 0;
while(index < input.length() && !isSign(input.charAt(index))) index++;
//如果整个式子里只包含数字,则直接返回存放当前数字的列表
if(index == input.length()){
res.add(Integer.parseInt(input.substring(0, index)));
return res;
}
//如果整个式子还包含运算符,则继续划分式子
for(int i = 0; i < input.length(); i++){
char ch = input.charAt(i);
//如果当前字符是运算符,则按该运算符将式子划分成两部
if(isSign(ch)){
//划分的左右两部分继续进入下一层递归,返回各自的计算结果集合
List<Integer> left = diffWaysToCompute(input.substring(0, i));
List<Integer> right = diffWaysToCompute(input.substring(i + 1));
//两集合内所有元素两两组合,按当前运算符计算最终结果,加入res
for(int j = 0; j < left.size(); j++){
for(int k = 0; k < right.size(); k++){
res.add(caculate(left.get(j), right.get(k), ch));
}
}
}
}
return res;
}
//判断当前字符是不是运算符:是运算符返回true,不是则返回false
public boolean isSign(char ch){
if(ch == '+' || ch == '-' || ch == '*') return true;
return false;
}
//返回两个int因子和char型符号的计算结果
public int caculate(int num1, int num2, char sign){
switch(sign){
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
default:
return -1;
}
}
}