第二十九天| 第七章 回溯算法part05 491. 递增子序列 46. 全排列 47. 全排列 II
一、491. 递增子序列
-
题目链接:https://leetcode.cn/problems/non-decreasing-subsequences/
-
题目介绍:
-
给你一个整数数组
nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
-
-
思路:
- 本题有三个关键点:
- (1)什么时候什么情况下收集结果集?
- 和子集问题一样,在遍历每个节点的开始位置,收集结果集
- 但是与自己问题不同的是,并不是所有的都可以添加
- 必须是路径**path中元素个数大于等于2的**
- (2)怎么判断这个子序列是递增的?也就是什么情况下会去掉不满足递增条件的子序列
- 如果path的末尾的元素数值大于待取元素数值,说明此时不满足递增条件
- (3)如何去重?
- 去重依旧是定位在一个集合,即树层去重
- 但是由于此时不能排序,因此需要定义一个哈希表记录加入的元素
- 如果待取元素在哈希表中出现过,说明重复了,要去重
- (1)什么时候什么情况下收集结果集?
- 根据上面的思路以及回溯三部曲,整理为以下:
- (1)确定参数和返回值
- 因为是在同一个数组中遍历,所以仍然需要startIndex
- (2)递归结束的条件
- 在递归结束条件之前收集结果集
- startIndex >= nums.length
- (3)单层搜索的逻辑
- (3.1)确定需要去掉的情况
- (3.1.1)去掉不满足递增的树枝:如果数组中待取的元素(即:nums[i])小于当前path末尾的元素, 需要去掉(因为不满足递增) 因为要使用path.size()所以要求其不为空
- (3.1.2)去重:依旧是定位在一个集合上,但是由于不能排序,所以需要定义一个set存放取出的数据,如果后续遍历当前集合的时候,发现set中出现了该值,就跳出该循环
- (3.2 )处理节点
- (3.3)递归
- (3.4)回溯
- (3.1)确定需要去掉的情况
- (1)确定参数和返回值
-
代码:
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return result;
}
// 1.确定参数和返回值
// 因为是在同一个数组中遍历,所以仍然需要startIndex
public void backtracking(int[] nums, int startIndex) {
// 收集结果集和子集问题一样,在每个节点处收集
if (path.size() >= 2) {
result.add(new ArrayList<>(path));
}
// 2.递归结束的条件
if (startIndex >= nums.length) {
return;
}
// 3.单层搜索的逻辑
HashSet<Integer> set = new HashSet<>();
for (int i = startIndex; i < nums.length; i++) {
// 3.1 确定需要去掉的情况
// 3.1.1 去掉不满足递增的树枝:如果数组中待取的元素(即:nums[i])小于当前path末尾的元素, 需要去掉(因为不满足递增) 因为要使用path.size()所以要求其不为空
// 3.1.2 去重:依旧是定位在一个集合上,但是由于不能排序,所以需要定义一个set存放取出的数据,如果后续遍历当前集合的时候,发现set中出现了该值,就跳出该循环
if (!path.isEmpty() && nums[i] < path.get(path.size() - 1) || set.contains(nums[i])) {
continue;
}
// 3.2 处理节点
path.add(nums[i]);
set.add(nums[i]);
// 3.3 递归
backtracking(nums, i + 1);
// 3.4 回溯
path.removeLast();
}
return;
}
}
二、46. 全排列
-
题目链接:https://leetcode.cn/problems/permutations/
-
题目介绍:
- 给定一个**不含重复数字**的数组
nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
- 给定一个**不含重复数字**的数组
-
思路:
- 区分排列和组合的区别
- 对于集合[1, 2]和[2, 1]
- 组合是一样的
- 排列是不同的
- 对于集合[1, 2]和[2, 1]
- 在代码中,我们不需要startInde控制下一次循环是从什么位置开始,我们每次从数组的index = 0的位置开始,只需要跳过已经记录在path中的节点即可。
- 区分排列和组合的区别
-
代码:
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
backtracking(nums);
return result;
}
// 1.确定返回值和参数
// 因为排列和组合不一样,如果两哥元素顺序不同表示不同的排列,但是是相同的组合
// 我们每次都是从数组的开始元素去取值,只需要跳过取过的值即可
public void backtracking(int[] nums) {
// 2.确定终止条件
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
// 3.单层递归的逻辑
for (int i = 0; i < nums.length; i++) {
// 3.1 跳过已经收集进来的节点
if (path.contains(nums[i])) {
continue;
}
// 3.2 处理节点
path.add(nums[i]);
// 3.3 递归
backtracking(nums);
// 3.4 回溯
path.removeLast();
}
return;
}
}
三、47. 全排列 II
-
题目链接:https://leetcode.cn/problems/permutations-ii/
-
题目介绍:
- 给定一个==可包含重复数字==的序列 nums ,按任意顺序 返回所有不重复的全排列。
-
思路:
- 因为数组中存在重复元素,并且要求解集中不能重复,所以要进行去重(去重还是先排序【一般的去重都要排序,除了“递增子序列”比较特殊】)
- 在树枝和树层中,都存在nums[i - 1] == nums[i]的情况,我们要进行树层去重
- 怎么定位到树层呢?
- 因为在树枝中,前一个和后一个数值可以相当,并且前后都取出来了,所以used[i - 1] 和 used[i]均为true
- 但是在树层中,use[i - 1] = false,因为前一个在此次循环中并没有取出
- 代码:
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
boolean[] used = new boolean[nums.length];
// 将used数组初始值全部赋值为false
// 取出了就变为true
Arrays.fill(used, false);
// 排列:去重的关键步骤
Arrays.sort(nums);
backtracking(nums, used);
return result;
}
// 1.确定返回值和参数
// 需要传入一个used数组记录元素的取出情况
public void backtracking(int[] nums, boolean[] used) {
// 2.结束递归的条件
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
// 3.单层递归的逻辑
for (int i = 0; i < nums.length; i++) {
// 3.1 去重
// 树层去重
if (i > 0 && nums[i - 1] == nums[i] && used[i - 1] == false) {
continue;
}
// 3.2 对于未取出的元素进行节点处理
if (used[i] == false) {
path.add(nums[i]);
used[i] = true;
// 3.3 递归
backtracking(nums, used);
// 3.4 回溯
path.removeLast();
used[i] = false;
}
}
return;
}
}