回朔算法是使用递归的方式,遍历所有的状态,一般借助数组等结构进行“剪枝”,较少遍历的次数。
解决的是 子集、组合、排列 问题。注意边界条件。
子集和排列和顺序无关,要借助位置信息防止重复;
排列和位置无关,只需判断辅助数据结构(path)中是否包含当前元素
下文介绍这几种的标准代码。
1、子集
力扣78题:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
class Solution {
List<List<Integer>> res = new ArrayList();
List<Integer> path = new ArrayList();
public List<List<Integer>> subsets(int[] nums) {
dfs(nums, 0);
return res;
}
// 注意index的用法
public void dfs(int[] nums, int index) {
res.add(new ArrayList(path));
for(int i=index;i<nums.length;i++) {
path.add(nums[i]);
dfs(nums, i+1);
path.remove(path.size()-1);
}
}
}
2、组合
组合是在子集的基础上进行剪枝,过程中不匹配直接返回,不在继续递归。
力扣77:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
class Solution {
List<List<Integer>> res = new ArrayList();
List<Integer> path = new ArrayList();
public List<List<Integer>> combine(int n, int k) {
dfs(n, k, 1);
return res;
}
public void dfs(int n, int k, int index) {
// 剪枝:组合是特定的子集,保留符合的
if(k == path.size()) {
res.add(new ArrayList(path));
return ;
}
for(int i=index;i<=n;i++) {
path.add(i);
dfs(n, k, i+1);
path.remove(path.size()-1);
}
}
}
3、排列
力扣46题: 全排列 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
class Solution {
List<List<Integer>> res = new ArrayList();
List<Integer> path = new ArrayList();
public List<List<Integer>> permute(int[] nums) {
dfs(nums);
return res;
}
public void dfs(int[] nums) {
if(path.size() == nums.length) {
res.add(new ArrayList(path));
return;
}
for(int i=0;i<nums.length;i++) {
if(path.contains(nums[i])) {
continue;
}
path.add(nums[i]);
dfs(nums);
path.remove(path.size()-1);
}
}
}