总结先放在前面:
回溯法抽象为树形结构后,其遍历过程就是:for循环横向遍历,递归纵向遍历,回溯不断调整结果集。
回溯算法的模板
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
根本思想就是在递归之前作出新的选择,然后撤回这个选择
解答回溯时的一些小技巧与注意点:
组合问题的一大特点就是它不要求返回结果的顺序
1.碰到乱序排列的数组,如何去重?
首先将数组进行排列使其变得有序,然后在for循环当中加入这个
if(i > cur && nums[i] == nums[i-1]){
continue;
}
其中nums指的是遍历的数组,cur指的是当前的起始位置。举例:NO.90. 子集 II
2.碰到子集问题,由于我们需要取所有叶子节点,所以要删除满足条件时的return
if 满足结束条件:
ans.Add(new List<int>(tmpList));
// return ;
// 注意这里不要加return,因为要取树上的所有节点
3.在排列问题当中,不需要起始点cur。需要一个数组来保存对应的nums[i]是否被使用过,在for循环当中注意回溯的同时回溯used[i],见NO.46. 全排列
题目实战
1.NO.77. 组合
public class Solution {
List<int> tmplist = new List<int>();
List<IList<int>> ans = new List<IList<int>>();
public IList<IList<int>> Combine(int n, int k) {
backTracking(n,k,1);
return ans;
}
public void backTracking(int n, int k, int cur){
//cur代表的是当前的起始位置
if(tmplist.Count==k){
ans.Add(new List<int>(tmplist));
return ;
}
for(int i = cur; i <= n ; i++){
tmplist.Add(i);
backTracking(n,k,i+1);
tmplist.RemoveAt(tmplist.Count-1);
}
}
}
2.NO.216.组合总和III
public class Solution {
List<int> tmpList = new List<int>();
List<IList<int>> ans = new List<IList<int>>();
public IList<IList<int>> CombinationSum3(int k, int n) {
//相当于77.组合问题中,遍历1-9,组合三个,筛选出和等于n的
backTraking(k,n,1);
return ans;
}
public void backTraking(int k, int n, int cur){
if(tmpList.Count==k){
if(getSum(tmpList)==n){
ans.Add(new List<int>(tmpList));
}
return ;
}
for(int i = cur; i <= 9; i++){
tmpList.Add(i);
backTraking(k,n,i+1);
tmpList.Remove(i);
}
}
public int getSum(List<int> tmpList){
int sum=0;
foreach(int i in tmpList){
sum+=i;
}
return sum;
}
}
3.NO.17. 电话号码的字母组合
public class Solution {
List<string> ans = new List<string>();
public IList<string> LetterCombinations(string digits) {
Dictionary<char, string> map = new