LeetCode 491 递增子序列
思路
递增子序列最少有两个元素,相同元素也视为递增。所以满足条件的节点不一定是叶子节点,因此加入结果集的操作也在主循环里。 考虑去重:
- 数组不可排序,相同的数字不一定相邻,故有可能遍历到相同的递增序列
- 考虑树结构,本题去重其实是在同一父节点下,兄弟节点直接不能选取相同的数字。即在一个同层的for循环内,不能选两次相同的数字。故可以在for循环内用局部set集合记录用过的数字。
考虑回溯三要素:
- 回溯函数的参数和返回值 参数:数组nums,开始索引start 返回值:空
- 回溯函数的结束条件 start等于nums长度,返回
- 回溯搜索的遍历过程
新建集合set,从start开始对每个元素nums[i]遍历
- 如果nums[i]存在于set中,跳过本次循环.
- 如果path不为空,且nums[i]大于等于path中最后一个元素则把元素加入路径,更新set,再把path加入result。如果不构成递增,跳过本次循环
- 如果path为空,直接把元素加入路径path,更新set
- 递归调用回溯函数
- 还原path
解法
class Solution {
List<List<Integer>> result;
List<Integer> path;
public List<List<Integer>> findSubsequences(int[] nums) {
result = new ArrayList<>();
path = new ArrayList<>();
backTracking(nums, 0);
return result;
}
public void backTracking(int[] nums, int start) {
if (start == nums.length) {
return ;
}
Set<Integer> set = new HashSet<>();
for (int i = start; i < nums.length; i++) {
if (set.contains(nums[i])) {
continue;
}
if (path.size() == 0) {
path.add(nums[i]);
set.add(nums[i]);
}
else {
if (nums[i] >= path.get(path.size()-1)) {
path.add(nums[i]);
set.add(nums[i]);
result.add(new ArrayList<>(path));
}
else {
continue;
}
}
backTracking(nums, i+1);
path.remove(path.size()-1);
}
}
}
LeetCode 46 全排列
思路
注意:排列有序,组合无序。所以组合需要使用start防止回头选择元素造成重复,但排列中回头选择会有不同的顺序,因此是不同的排列,无需使用start。每次for循环都从0开始。 虽然无需规定开始索引,但数组中的数字不可重复使用,我们需要判断一个数字是否使用过,引入inPath布尔数组
考虑回溯三要素:
- 回溯函数的参数和返回值 参数:nums 返回值:空 全局变量:result,path,inPath,还没使用的元素个数left
- 回溯函数的结束条件 当left为0。把path加入result,返回
- 回溯搜索的遍历过程
对inPath中每个值为假的索引i:
- 把nums[i]加入path,inPath[i]置为真,left-1
- 递归调用回溯函数
- 还原path,inPath[i]置为假,left+1
解法
class Solution {
List<List<Integer>> result;
List<Integer> path;
boolean[] inPath;
int left;
public List<List<Integer>> permute(int[] nums) {
result = new ArrayList<>();
path = new ArrayList<>();
inPath = new boolean[nums.length];
left = nums.length;
backTracking(nums);
return result;
}
public void backTracking(int[] nums) {
if (left == 0) {
result.add(new ArrayList<>(path));
}
for (int i = 0; i < nums.length; i++) {
if (inPath[i]) {
continue;
}
path.add(nums[i]);
inPath[i] = true;
left--;
backTracking(nums);
left++;
inPath[i] = false;
path.remove(path.size()-1);
}
}
}
LeetCode 47 全排列 II
思路
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
考虑去重:
- 此题中可以改变元素顺序,所以我们先对数组排序
- 排列需要所有数字都存在,不同的排列来自于对元素的选择顺序不同,所以我们只需要在搜索树中同层不选择相同元素即可。使用集合记录本层使用过的数字,如果存在就跳过
考虑回溯三要素:
- 回溯函数的参数和返回值 参数:数组 返回值:空 全局变量:result,path,inPath,left剩余元素数量
- 回溯函数的结束条件 left为0,把path加入result,返回
- 回溯搜索的遍历过程
初始化局部变量set,对数组中每个元素nums[i]:
- 如果inPath[i]为真,跳过
- 如果nums[i]在集合中,跳过
- 把nums[i]加入path,更新inPath,更新left,nums[i]加入set
- 递归调用回溯函数
- 还原path,inPath,left
解法
class Solution {
List<List<Integer>> result;
List<Integer> path;
int left;
boolean[] inPath;
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
result = new ArrayList<>();
path = new ArrayList<>();
left = nums.length;
inPath = new boolean[nums.length];
backTracking(nums);
return result;
}
public void backTracking(int[] nums) {
if (left == 0) {
result.add(new ArrayList<>(path));
return ;
}
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if (inPath[i]) {
continue;
}
if (set.contains(nums[i])) {
continue;
}
set.add(nums[i]);
path.add(nums[i]);
inPath[i] = true;
left--;
backTracking(nums);
left++;
inPath[i] = false;
path.remove(path.size()-1);
}
}
}