今日收获:递增子序列,全排列,全排列Ⅱ,重新安排行程,N皇后,解数独
1. 递增子序列
题目链接:491. 非递减子序列 - 力扣(LeetCode)
思路:
1. 不能选同一层相同元素,可以选同一个树枝上不同位置的相同元素。之前这样要求的去重逻辑是对原数组排序后,用同样大小的数组标记。本题不能对数组排序,所以要用哈希法去重。
2. 搜集树中所有的子节点
3. 如果path不为空且当前节点小于上一个路径节点(不满足递增子序列的条件),或者本元素已经使用过了(同一层元素重复了),则跳过本节点(即本次循环)。
方法:
class Solution {
List<List<Integer>> result=new ArrayList<>();
List<Integer> path=new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
back(nums,0);
return result;
}
public void back(int[] nums,int start){
if (path.size()>=2){ // 至少有两个元素,取所有子节点
result.add(new ArrayList<>(path));
}
HashSet<Integer> used=new HashSet<>();
for (int i=start;i<nums.length;i++){
int len=path.size();
// 确保当前选择的元素大于等于路径的上一个元素并去除重复
if (len>0&&nums[i]<path.get(len-1)||used.contains(nums[i])){
continue;
}
path.add(nums[i]);
used.add(nums[i]);
back(nums,i+1);
path.removeLast();
}
}
}
总结:
1. 当不能对数组排序且需要去重同一层节点时,需要用哈希法去重。在用数组去重时,每一个元素都有唯一的位置标识,所以每个回溯函数共享使用数组。但是用哈希法时每个回溯函数都要新建一个哈希集合,确保去重只在本层生效;不能共享,否则对每一条路径上的元素也去重了。
2. 当前节点不满足特定条件就跳过本次循环,continue下一个更乖!
2. 全排列
思路:使用used数据记录选取过的元素,每一次都要从头遍历数组选取未使用过的元素。终止条件是所有的元素都用完了
方法:
class Solution {
List<Integer> path=new ArrayList<>();
List<List<Integer>> result=new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
int[] used=new int[nums.length];
for (int i=0;i<nums.length;i++){
used[i]=0;
}
back(nums,used);
return result;
}
public void back(int[] nums,int[] used){
int len=nums.length;
// 所有节点都用完了
if (path.size()==len){
result.add(new ArrayList<>(path));
return;
}
for (int i=0;i<len;i++){
if (used[i]==1){
continue;
}
path.add(nums[i]);
used[i]=1;
back(nums,used);
path.removeLast();
used[i]=0;
}
}
}
3. 全排列Ⅱ
题目链接:47. 全排列 II - 力扣(LeetCode)
思路:在上一题的基础上添加同一层去重的逻辑
方法:
class Solution {
List<Integer> path=new ArrayList<>();
List<List<Integer>> result=new ArrayList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
int[] used=new int[nums.length];
Arrays.fill(used,0);
back(nums,used);
return result;
}
public void back(int[] nums,int[] used){
int len=nums.length;
// 所有节点都用完了
if (path.size()==len){
result.add(new ArrayList<>(path));
return;
}
for (int i=0;i<len;i++){
// 同一层使用过
if (i>0&&nums[i]==nums[i-1]&&used[i-1]==0){
continue;
}
if (used[i]==0){ // 同一个树枝上没有使用过
path.add(nums[i]);
used[i]=1;
back(nums,used);
path.removeLast();
used[i]=0;
}
}
}
}
总结:需要记忆同一层去重的固定写法
if (i>0&&nums[i]==nums[i-1]&&used[i-1]==0){
continue;
}
4. 重新安排行程(一刷了解思路)
题目链接:332. 重新安排行程 - 力扣(LeetCode)
二刷一定可以啃下来!
5. N皇后(一刷了解思路)
二刷一定可以啃下来!
6. 解数独(一刷了解思路)
二刷一定可以啃下来!