回溯算法
视频讲解
回溯是一个增量构造答案的过程(这个过程用递归实现)
i
i
i表示大于等于
i
i
i
所有回溯法的问题都可以抽象为树形结构!
回溯三部曲:
1.进行选择 2.递归 3.撤回选择
Leecode题目
491. 递增子序列
而本题求自增子序列,是不能对原数组进行排序的
本题求子序列,很明显一个元素不能重复使用,所以需要startIndex
这个讲解的很好,利用HashSet去重
class Solution {
public List<List<Integer>> findSubsequences(int[] nums) {
HashSet<List<Integer>> r = new HashSet<>();
LinkedList<Integer> path = new LinkedList<>();
backtracking(r,path,nums,0);
List<List<Integer>> res = new ArrayList(r);
return res;
}
private void backtracking(HashSet<List<Integer>> res,LinkedList<Integer> path ,int[] nums, int start){
if(path.size()>=2){
res.add(new ArrayList(path));
}
for(int i = start;i<nums.length;i++){
if(path.size()==0||path.getLast()<=nums[i]){
path.add(nums[i]);
backtracking(res,path,nums,i+1);
path.removeLast();
}
}
}
}
46. 全排列
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
Deque<Integer> stack = new ArrayDeque<>();
boolean[] used = new boolean[len]; //初始化默认全是false;表示都没有被用过
if(len == 0){
return res;
}
dfs(stack,used,nums,res,0,len);
return res;
}
public void dfs(Deque<Integer> stack, boolean[] used, int[] nums, List<List<Integer>> res,int depth, int len){
//终止条件
if(depth == len){
res.add(new ArrayList<>(stack));
return;
}
//1.选择(用for循环)
for(int i=0;i<len;i++){
if(!used[i]){
stack.push(nums[i]); // stack 又名 path, 这里还可以改写为 path.addLast(nums[i])
used[i]=true;//添加到路径中就表示已经被使用了
//2.递归
dfs(stack,used,nums,res,depth+1,len);
//3.撤回选择
used[i]=false;
stack.pop(); // // stack 又名 path, 这里还可以改写为 path.removeLast()
}
}
}
}
s
t
a
c
k
stack
stack 又名
p
a
t
h
path
path:
s
t
a
c
k
.
p
u
s
h
(
n
u
m
s
[
i
]
)
stack.push(nums[i])
stack.push(nums[i]) 改写为
p
a
t
h
.
a
d
d
L
a
s
t
(
n
u
m
s
[
i
]
)
path.addLast(nums[i])
path.addLast(nums[i])
s
t
a
c
k
.
p
o
p
(
)
stack.pop()
stack.pop()改写为
p
a
t
h
.
r
e
m
o
v
e
L
a
s
t
(
)
path.removeLast()
path.removeLast()
其中的depth其实就是stack的大小
class Solution {
List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
if (nums.length == 0){
return result;
}
used = new boolean[nums.length];
permuteHelper(nums);
return result;
}
private void permuteHelper(int[] nums){
if (path.size() == nums.length){
result.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++){
if (used[i]){
continue;
}
used[i] = true;
path.add(nums[i]);
permuteHelper(nums);
path.removeLast();
used[i] = false;
}
}
}
47. 全排列
这一道题与46的区别:需要去重代码随想录的解析
对于同一树层,不能出现相同的元素(used[i-1]==false,因为回溯之后会把对应的used设为false)
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
int len = nums.length;
Deque<Integer> path = new ArrayDeque<>();
boolean[] used = new boolean[len];
if(len==0){
return res;
}
dfs(path,res,nums,used);
return res;
}
private void dfs( Deque<Integer> path, List<List<Integer>> res, int[] nums,boolean[] used){
if(path.size()==nums.length){
res.add(new ArrayList<>(path));
return;
}
// 1.选择,树的第一层
for(int i=0;i<nums.length;i++){
//去重,树层中不可以有相同的元素
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
continue; //表示相同的元素上一个用过了
}
// 树枝处理开始(从没有用过的开始放入树枝)
if(!used[i]){
path.addLast(nums[i]);
used[i]=true;
dfs(path,res,nums,used);
path.removeLast();
used[i]=false;
}
}
}
}