491.递增子序列
题目链接:https://leetcode.cn/problems/non-decreasing-subsequences/
文档讲解:https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F…
视频讲解:https://www.bilibili.com/video/BV1EG4y1h78v/
思路
- 由于本题不能变换数组顺序,所以在for循环之前用一个
set
来记录之前放入数组中的元素,同层的相同数就不能重复取了。 - 还有一个需要跳过的条件是如果当前数比path的最后一个数小,就跳过。
if ((path.size() > 0 && nums[i] < path.getLast()) || set.contains(nums[i])) continue;
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return res;
}
public void backtracking(int[] nums, int startIndex) {
if (path.size() >= 2) {
res.add(new ArrayList<>(path));
}
Set<Integer> set = new HashSet<>();
for (int i = startIndex; i < nums.length; i++) {
if ((path.size() > 0 && nums[i] < path.getLast()) || set.contains(nums[i])) continue;
set.add(nums[i]);
path.add(nums[i]);
backtracking(nums, i + 1);
path.removeLast();
// set不用回溯,因为for循环中是同层的逻辑,下一层的set是新的
}
}
}
46.全排列
题目链接:https://leetcode.cn/problems/permutations/
文档讲解:https://programmercarl.com/0046.%E5%85%A8%E6%8E%92%E5%88%97.html
视频讲解:https://www.bilibili.com/video/BV19v4y1S79W/
思路
排列问题与组合问题的不同:
- 每层都是从
0
开始搜索而不是startIndex
。 - 需要
used
数组记录path里都放了哪些元素了或者判断path
中是否包含当前元素。
代码
不使用used数组
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
backtracking(nums);
return res;
}
public void backtracking(int[] nums) {
if (path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (path.contains(nums[i])) continue;
path.add(nums[i]);
backtracking(nums);
path.removeLast();
}
}
}
使用used数组
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
used = new boolean[nums.length];
Arrays.fill(used, false);
backtracking(nums);
return res;
}
public void backtracking(int[] nums) {
if (path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) continue; // 被用过就是true,就会continue
used[i] = true;
path.add(nums[i]);
backtracking(nums);
path.removeLast();
used[i] = false;
}
}
}
47.全排列 II
题目链接:https://leetcode.cn/problems/permutations-ii/
文档讲解:https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html
视频讲解:https://www.bilibili.com/video/BV1R84y1i7Tm/
思路
- 本题数组中有重复元素,所以又要使用
used
数组来去重。
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue; // 树层去重
而在排列问题中,used[i - 1] == true
也行,used[i - 1] == false
也行。 对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高。树层上对前一位去重非常彻底,效率很高,树枝上对前一位去重虽然最后可以得到答案,但是做了很多无用搜索。
- 因为排列问题中的循环是从0开始的,所以也要对已经放入
path
中的结果去重。
if (used[i] == true) continue; // 已经用过的数不能再用
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
Arrays.fill(used, false);
Arrays.sort(nums);
backtracking(nums);
return res;
}
public void backtracking(int[] nums) {
if (path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue; // 树层去重
if (used[i] == true) continue; // 已经用过的数不能再用
used[i] = true;
path.add(nums[i]);
backtracking(nums);
path.removeLast();
used[i] = false;
}
}
}