回溯算法其实就是递归,了解递归的朋友一般就非常容易理解回溯算法
回溯算法一般有三部分构成
1.初始传参数
2.回溯函数,满足条件 返回对应的值
3.确定终止条件
例题1全排列 力扣46题
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Deque<Integer> deque = new ArrayDeque<>();
int len = nums.length;
if (nums.length == 0){
return res;
}
int nowIdenx = 0;
//1.初始传参数
backtrack(nowIdenx,deque,nums,res,len);
return res;
}
//2.回溯函数,满足条件 返回对应的值
private void backtrack(int nowIdenx, Deque<Integer> deque, int[] nums, List<List<Integer>> res, int len) {
//3.确定终止条件
if (deque.size() == len){
res.add(new ArrayList<Integer>(deque));
return;
}
for (int i = 0; i<nums.length ; i++){
if (deque.contains(nums[i])){
continue;
}
deque.addLast(nums[i]);
backtrack(i,deque,nums,res,len);
deque.removeLast();
}
}
}
解析:递归可以看成一种进栈出栈(堆栈,数据结构说堆栈和栈是一个意思)的操作,先进后出:用图表示,因为比较好理解,下次看直接就能想起来
然后进入下一层递归函数,进入递归函数中是判断是否满足终止条件,这里不满足,然后进入for循环中,这里i是从0开始的,为了避免重复,这里有一个判断条件,deque中是否存在nums[i],因为不能重复,这里deque双向队列,用来当stack用了,直接用stack也行,个人习惯,因为nums【0】已经存在了,所以i++,i=1;条件成立,先把i放入栈(deque表示的栈)中,再次进入backstack函数中;
同理:i++,deque.addLast(3);
再次进入backtrack(这个英文是回溯的意识)函数中,这里deque.size==len(条件)时,return:结束这次backtrack函数,这里要说的是,结束时去哪里,其实就是递归的基础,
for (int i = 0; i<nums.length ; i++){
if (deque.contains(nums[i])){
continue;
}
//因为nums[2]进入了backtrack函数
deque.addLast(nums[i]);
//backtrack(i,deque,nums,res,len);
//这里我们把函数展开;nums[2]==3进入backtrack函数
//3.确定终止条件 条件成立,res添加条件满足的结果,看图
if (deque.size() == len){
res.add(new ArrayList<Integer>(deque));
//return;
}
//因为上一个backtrack 已经return了,所以会走到这里
deque.removeLast();
//从栈中移除nums[2]这个数字
}
下面该怎么走呢?其实很简单,随着第三次backtrack函数的结束,递归跳的第二次backstack中,这是i还是等于num[1],因为
//3.确定终止条件
if (deque.size() == len){
res.add(new ArrayList<Integer>(deque));
return;
}
for (int i = 0; i<nums.length ; i++){
if (deque.contains(nums[i])){
continue;
}
//这时候i等于1,以为上一个backtrack函数的结束成功
deque.addLast(nums[i]);
backtrack(i,deque,nums,res,len);
//因为backtrack函数的成功结束,deque(栈)中的2也会被弹出
deque.removeLast();
}
聪明的你,应该已经明白了,这道题还可以剪枝
下面就是一道例题;力扣77题 组合 这个题用到了剪枝,for需要哪里
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (n<k || k<=0){
return res;
}
Deque<Integer> deque = new ArrayDeque<>();
int nowIndex = 1;
backtoto(n,k,nowIndex,deque,res);
return res;
}
private void backtoto(int n, int k, int nowIndex, Deque<Integer> deque, List<List<Integer>> res) {
if (deque.size()==k){
res.add(new ArrayList<>(deque));
return;
}
//这里就用到了剪枝
for (int i = nowIndex;i <= n-(k-deque.size())+1;i++){
deque.addLast(i);
backtoto(n,k,i+1,deque,res);
deque.removeLast();
}
}
}