-
- 全排列 II
问题描述
给定一个可能包含重复数字的数组 nums,生成所有唯一的排列。
代码逻辑
初始化:
对数组进行排序(Arrays.sort(nums)),以便后续去重。
使用 List<List> res 存储最终结果。
使用 LinkedList path 存储当前路径。
使用布尔数组 used 标记数组中的数字是否被使用。
递归函数 dfsPermuteUnique:
如果 path 的长度等于数组长度,说明已经生成一个完整的排列,将其加入结果集。
遍历数组:
如果当前数字已经被使用(used[i]),跳过。
如果当前数字与下一个数字相同且下一个数字未被使用(nums[i] == nums[i + 1] && !used[i + 1]),跳过(避免重复排列)。
将当前数字加入路径(path.add(nums[i])),标记为已使用(used[i] = true)。
递归调用 dfsPermuteUnique。
回溯:移除路径中的最后一个数字(path.removeLast()),恢复未使用状态(used[i] = false)。
关键点
排序:通过排序确保相同的数字相邻,便于去重。
去重条件:nums[i] == nums[i + 1] && !used[i + 1] 是核心去重逻辑,确保只选择第一个未被使用的重复数字。
点击查看代码
//47. 全排列 II
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
boolean[] used = new boolean[nums.length];//布尔型默认值为false
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
dfsPermuteUnique(nums,used,res,path);
return res;
}
private void dfsPermuteUnique(int[] nums,boolean[] used ,List<List<Integer>> res, LinkedList<Integer> path) {
if (path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
}
for(int i=0;i<nums.length;i++){
if (used[i]) continue;
if(i+1<nums.length&&nums[i]==nums[i+1]&&!used[i+1]) continue;//之所以加上&&!used[i+1]是确保这后面那个元素是它可以取到的
path.add(nums[i]);
used[i] = true;//表示已用
dfsPermuteUnique(nums,used, res, path);
path.removeLast();
used[i] = false;//还原为未用状态
}
}
-
- 全排列
问题描述
给定一个不包含重复数字的数组 nums,生成所有可能的排列。
代码逻辑
初始化:
使用 List<List> res 存储最终结果。
使用 LinkedList path 存储当前路径。
使用布尔数组 used 标记数组中的数字是否被使用。
递归函数 dfsPermute:
如果 path 的长度等于数组长度,说明已经生成一个完整的排列,将其加入结果集。
遍历数组:
如果当前数字已经被使用(used[i]),跳过。
将当前数字加入路径(path.add(nums[i])),标记为已使用(used[i] = true)。
递归调用 dfsPermute。
回溯:移除路径中的最后一个数字(path.removeLast()),恢复未使用状态(used[i] = false)。
关键点
布尔数组 used:用于标记数组中的数字是否被使用,避免重复选择同一个数字。
递归终止条件:当路径长度等于数组长度时,生成一个完整的排列。
优化建议
由于数组中没有重复数字,不需要额外的去重操作。
可以通过剪枝减少不必要的计算。
总结
这是一个经典的回溯问题,通过布尔数组标记使用状态,生成所有可能的排列。
难点在于如何通过递归和回溯生成所有排列,布尔数组是解决此问题的关键。
点击查看代码
//46. 全排列
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
boolean[] used = new boolean[nums.length];
dfsPermute(nums,used,res,path);
return res;
}
private void dfsPermute(int[] nums, boolean[] used,List<List<Integer>> res, LinkedList<Integer> path) {
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;两种判断方式都可以,下面那个更具普适性
if (used[i]) continue;//用过的不可取,跳过
path.add(nums[i]);
used[i] = true;//表示已用
dfsPermute(nums,used ,res, path);
path.removeLast();
used[i] = false;//还原为未用状态
}
}
-
- 非递减子序列
问题描述
给定一个整数数组 nums,找出所有长度至少为2的非递减子序列。数组中的元素顺序不能改变,且结果中不能有重复的子序列。
代码逻辑
初始化:
使用 List<List> res 存储最终结果。
使用 LinkedList path 存储当前路径(即当前构建的子序列)。
从索引 start 开始递归搜索。
递归函数 dfsFindSubsequences:
如果 path 的长度大于1,则将当前路径加入结果集(res.add(new ArrayList<>(path)))。
如果 start 达到数组末尾,返回。
使用 ArrayList used 记录当前层级已经使用过的数字,避免重复。
遍历数组:
如果当前数字小于 path 的最后一个数字(nums[i] < path.peekLast()),跳过。
如果当前数字已经在当前层级使用过(used.contains(nums[i])),跳过。
将当前数字加入路径(path.add(nums[i])),标记为已使用(used.add(nums[i]))。
递归调用 dfsFindSubsequences,从下一个索引开始。
回溯:移除路径中的最后一个数字(path.removeLast())。
关键点
非递减条件:通过 nums[i] >= path.peekLast() 确保子序列非递减。
去重:通过 used 集合记录当前层级已经使用过的数字,避免重复子序列。
优化建议
used.contains(nums[i]) 使用了线性查找,可以改用 HashSet,将查找时间复杂度从 O(n) 优化到 O(1)。
总结
这是一个典型的回溯问题,通过递归和回溯生成所有可能的子序列,并通过条件判断和去重操作确保结果符合要求。
难点在于如何处理重复子序列,使用集合记录当前层级的数字是一种有效的去重方法。
点击查看代码
//491. 非递减子序列
public List<List<Integer>> findSubsequences(int[] nums) {
//Arrays.sort(nums);不能改变数组中元素顺序
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
dfsFindSubsequences(nums,res,path,0);
return res;
}
private void dfsFindSubsequences(int[] nums,List<List<Integer>> res,LinkedList<Integer> path ,int start){
if (path.size()>1) res.add(new ArrayList<>(path));
if(start==nums.length) return;
ArrayList<Integer> used =new ArrayList<>();
for(int i=start;i<nums.length;i++){
//if (i>start && nums[i]==nums[i-1]) continue;不是有序的就不能这样去重了,只能弄个集合来记录这一层级已经使用过的数字
if ((!path.isEmpty()&&nums[i]<path.peekLast())||used.contains(nums[i])) continue;
path.add(nums[i]);
used.add(nums[i]);
dfsFindSubsequences(nums,res,path,i+1);
path.removeLast();
}
}