因为在实习,每天只有晚上有时间,回来之后总是卡在[startIndex,i]的问题上,一直推到周末终于想通了,记录一下。
这个题目我遇到的想不通的点是为什么切割的子字符串是[startIndex,i]。因为切割和组合问题是相似的,所以我们可以用最简单的组合问题来考虑。
回到我们之前做的第一个回溯组合问题,我们把他再简化,从[1,n]里面找2个树的组合。
这里的startIndex的作用是去重,取过的数不再重复取。可以这样来看,我们只看这个树的纵向最左边,这条线代表的是startIndex从1开始,也就是第一个取了1,然后进入递归之后传给下一层递归的是i+1,也就是2,代表的是下一层递归要从【2,3,4】里面去取,取完2之后,也就是纵向最左边外层走完了,满足条件开始return,然后回溯,路径中去掉2,只剩1,此时return回去之后startIndex是2,for循环开始往下走++变成了3,也就是取到了【1,3】。然后去循环,去递归。
所以要理解回溯,一定要理解他画出来的树形结构,startIndex代表的是纵向递归中从哪里开始取,当然也包括回溯之后下一个从哪开始走(比如第一次回溯之后,startInded=2,i++取3)。
那么回到切割问题,这时候我们就明确了,每次我们要切割的区间,其实就是【startIndex,i】。
【1,2】,【1,【1,3】】,【1,【1,4】】,【【1,2】,3】,【【1,2】,【3,4】】。。。
class Solution {
List<List<Integer>> result= new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
backtracking(n,k,1);
return result;
}
public void backtracking(int n,int k,int startIndex){
if (path.size() == k){
result.add(new ArrayList<>(path));
return;
}
for (int i =startIndex;i<=n;i++){
path.add(i);
backtracking(n,k,i+1);
path.removeLast();
}
}
}
startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置
```
class Solution {
List<String> res = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
backtracking(s,0,0);
return res;
}
public void backtracking(String s,int startIndex,int sumPoint){
if(sumPoint==3){
//看看最后一个.后面的是不是合法的
if(isValid(s,startIndex,s.length()-1)){
res.add(s);
}
return;
}
for(int i=startIndex;i<s.length();i++){
if(isValid(s,startIndex,i)){
s=s.substring(0,i+1)+"."+s.substring(i+1);
sumPoint++;
backtracking(s,i+2,sumPoint);
sumPoint--;
s=s.substring(0,i+1)+s.substring(i+2);
}else{
break;
}
}
}
public boolean isValid(String s,int start,int end){
if(start>end){
return false;
}
if(s.charAt(start)=='0'&&start!=end){
return false;
}
int num=0;
for(int i=start;i<=end;i++){
if(s.charAt(i)>'9'||s.charAt(i)<'0'){
return false;
}
num=num*10+(s.charAt(i)-'0');
if(num>255){
return false;
}
}
return true;
}
}
```