解决一个回溯问题,实际上就是一个决策树的遍历问题
全排列问题
多叉树遍历框架
void traverse(TreeNode root){
for (TreeNode child : root.children){
//前序遍历操作
traverse();
//后序遍历操作
}
}
回溯算法框架
for 选择 in 选择列表:
# 做选择
将该选择从选择列表中移除
路径.add(选择)
backtrack(路径,选择列表)
# 撤销选择
路径.remove(选择)
将该选择恢复到选择列表
例题
【leetcode46 全排列】
class Solution {
List<List<Integer>> res = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
//记录 “路径”
LinkedList<Integer> path = new LinkedList<>();
backtrack(nums, path);
return res;
}
// 路径:记录在path中
// 选择列表:nums中不存在于path的那些元素
// 结束条件:nums中的元素全部都在path中
void backtrack(int[] nums, LinkedList<Integer> path){
//触发结束条件
if(path.size()==nums.length){
res.add(new LinkedList(path));
return;
}
for(int i=0;i<nums.length;i++){
//排除不合法的选择
if(path.contains(nums[i]))
continue;
//做选择
path.add(nums[i]);
//进入下一层决策
backtrack(nums,tmp);
//取消选择
path.removeLast();
}
}
}
【leetcode47 全排列II】
class Solution {
List<List<Integer>> res = new LinkedList<>();
boolean[] vis;
public List<List<Integer>> permuteUnique(int[] nums) {
LinkedList<Integer> tmp = new LinkedList<>();
vis = new boolean[nums.length];
backtrack(nums, tmp);
return res;
}
void backtrack(int[] nums, LinkedList<Integer> tmp){
Arrays.sort(nums);
if(tmp.size()==nums.length){
res.add(new LinkedList(tmp));
return;
}
for(int i=0;i<nums.length;i++){
if(vis[i]==true || (i>0 && nums[i]==nums[i-1] && vis[i-1]==false))
// 对原数组排序,然后每次填入的数一定是这个数所在重复数集合中[从左往右第一个未被填过的数字]
continue;
tmp.add(nums[i]);
vis[i] = true;
backtrack(nums,tmp);
vis[i] = false;
tmp.removeLast();
}
}
}
【leetcode17 电话号码的字母组合】
class Solution {
List<String> res = new ArrayList<String>();
List<String> combinnation = new ArrayList<String>();
HashMap<Character,String> map = new HashMap<Character,String>(){{
put('2',"abc");
put('3',"def");
put('4',"ghi");
put('5',"jkl");
put('6',"mno");
put('7',"pqrs");
put('8',"tuv");
put('9',"wxyz");
}};
public List<String> letterCombinations(String digits) {
if(digits.length()==0){
return res;
}
for(int i=0;i<digits.length();i++){
combinnation.add(map.get(digits.charAt(i)));
}
backtrack(new StringBuffer(),digits,0);
return res;
}
void backtrack(StringBuffer path,String digits,int index){
if(index==digits.length()){
res.add(path.toString());
return;
}
for(int i=0;i<combinnation.get(index).length();i++){
path.append(combinnation.get(index).charAt(i));
backtrack(path,digits,index+1);
path.deleteCharAt(index);
}
}
}
【leetcode77 组合】
class Solution {
List<List<Integer>> res = new LinkedList<>();
boolean[] vis;
public List<List<Integer>> combine(int n, int k) {
LinkedList<Integer> track = new LinkedList<Integer>();
vis = new boolean[n];
int[] nums = new int[n];
for(int i=0;i<n;i++){
nums[i] = i+1;
}
backtrack(nums,track,k,0);
return res;
}
void backtrack(int[] nums, LinkedList<Integer> track,int k,int index){
if(track.size()==k){
res.add(new LinkedList(track));
return;
}
for(int i=index;i<nums.length;i++){
if(vis[i] == true){
continue;
}
track.add(nums[i]);
vis[i] = true;
backtrack(nums,track,k,i+1);
vis[i] = false;
track.removeLast();
}
}
}