回溯法:
一种通过探索所有可能的候选解来找出所有的解的算法
如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化抛弃该解,即回溯并且再次尝试
java
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
// output:当前正在构造的排列数组,一开始直接把nums全部放进来
List<Integer> output = new ArrayList<Integer>();
for (int num : nums) {
output.add(num);
}
int n = nums.length;
// 回溯入口:从第0个位置开始填数
backtrack(n, output, res, 0);
return res;
}
public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
// 终止条件:first == n,说明 [0 ~ n-1] 所有位置都已经填好了,得到一个完整排列
if (first == n) {
// 重点:不能直接add(output),要new一个新ArrayList拷贝快照
// 因为后面swap会不断修改output对象,直接存引用会全部被改乱
res.add(new ArrayList<Integer>(output));
}
for (int i = first; i < n; i++) {
//相当于选定这个数放在first
Collections.swap(output, first, i);
// 递归:first位置已经确定,去处理下一个位置 first+1
backtrack(n, output, res, first + 1);
// 【回溯/撤销】交换回来,恢复现场,尝试下一个i
// 这一步是回溯核心:递归结束后,把刚才的交换复原,不影响后面循环
Collections.swap(output, first, i);
}
}
}
本质就是: 把所有合法的排列情况,不漏、不重地全部枚举一遍
为什么不用for循环遍历呢
for 循环的层数必须写死;但排列的长度 n 是变量,你不知道要写几层 for,所以写不了