分治+后序遍历+记忆化搜索
解题思路:时间复杂度O(
2
n
2^n
2n),每个运算符两边都需要使用后序遍历进行分治,空间复杂度O(
2
n
2^n
2n) |
---|
- 遍历字符串遇到运算符x后,就以x为中心分成左右两部分,这两部分都不包含当前x这个运算符
- 对两部分不断进行分治,遇到运算符就分为两部分。直到当前进行分治的字符串是纯数字,没有运算符,就返回这个数字
- 当我们接收到分治处理完成的数字列表时,对左右两部分的数字,用运算符x进行枚举组合。这样就完成了枚举所有运算符不同优先级的不同结果
- 但是分治过程中,会发现有很多重复操作,所以我们可以用map保存每次分治结果,如果下次是相同的分治过程,就直接返回map中保存的结果
- 找到第一个运算符2
*
3-4 * 5,将其分为两部分,2和3-4 * 5,分别对两部分进行后序遍历。其中左边部分是2,不继续遍历 - 3
-
4 * 5分成两部分为3和4 * 5,左边部分是3,不继续遍历 - 4
*
5分成两部分为4和5,都不进行遍历,然后参与运算获取(4 * 5)
- 回到3
-
4 * 5,参与运算获得3 - (4 * 5)
继续分治,这次找第二个运算符3-4 *
5,分成两部分3-4和5.右边部分为5不继续遍历 - 3
-
4分成两部分,都是数字,然后参与运算获得(3-4)
- 回到3-4
*
5,此时左边为第5步获得的(3-4),参与运算获得(3-4) * 5
- 回到2
*
3-4 * 5,此时左边为2,右边为(3-4) * 5
和3 - (4 * 5)
。参与运算获得第一个答案:2 * ((3-4) * 5)
和第二个答案:2 * (3 - (4 * 5))
然后找第二个运算符2 * 3-
4 * 5继续左右两边2 * 3和4 * 5进行后序遍历分治
- 分治后获得
(2*3)
和(4*5)
,回到2 * 3-
4 * 5运算后得到第3个答案:(2 * 3) - (4 * 5)
- 回到2 * 3
-
4 * 5,找第3个运算符2 * 3-4 *
5,分两部分为2 * 3-4和5 - 2 * 3-4
- 第一个运算符2
*
3-4,获得(2 * (3-4))
- 第二个运算符2 * 3
-
4,获得((2 * 3)-4))
- 2 * 3-4
*
5,左边有两个结果(2 * (3-4))
和((2 * 3)-4))
获得第4个答案:(2 * (3-4))*5
和第5个答案:((2 * 3)-4))*5
class Solution {
Map<String, List<Integer>> map=new HashMap<>();
public List<Integer> diffWaysToCompute(String expression) {
return dfs(expression);
}
public int parseInt(String str){
for(int i=0;i<str.length();i++){
if(!Character.isDigit(str.charAt(i)))return -1;
}
return Integer.parseInt(str);
}
public List<Integer> dfs(String str){
int num=parseInt(str);
if(num>=0) return new ArrayList<Integer>(){{add(num);}};
if(map.containsKey(str))return map.get(str);
List<Integer> res = new ArrayList<>();
for(int i=0;i<str.length();i++){
char ch=str.charAt(i);
if(ch=='+' || ch=='-' || ch=='*'){
List<Integer> left = dfs(str.substring(0,i));
List<Integer> right = dfs(str.substring(i+1));
int val=0;
for(int l:left){
for(int r:right){
switch(ch){
case '+':
val=l+r;
break;
case '-':
val=l-r;
break;
case '*':
val=l*r;
break;
default:
}
res.add(val);
}
}
}
}
map.put(str, res);
return res;
}
}