491.递增子序列
题目链接:Loading Question... - 力扣(LeetCode)
解题思路:
首先是想到只要后插入的元素比先插入path数组的元素大就可以了,但是这样会存在重复解:
例如示例[4,6,7,7] 就会存在重复解[4,6,7] [4,6,7]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backTracking(nums, 0);
return ans;
}
public void backTracking(int[] nums, int startIndex){
if(path.size() >= 2) ans.add(new ArrayList<>(path));
for(int i = startIndex; i < nums.length; i++){
if(path.size() > 0 && path.get(path.size() - 1) > nums[i]) continue;
path.add(nums[i]);
backTracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
然后考虑到记录一下同一层上一个元素,通过它来继续筛掉重复项,这个方法确实可以筛掉如[4,6,7,7]案例中的[4,6,7],[4,6,7]这种重复项。改进如下:
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new LinkedList<>();
int flag;
public List<List<Integer>> findSubsequences(int[] nums) {
backTracking(nums, 0);
return ans;
}
public void backTracking(int[] nums, int startIndex){
if(path.size() >= 2) ans.add(new ArrayList<>(path));
for(int i = startIndex; i < nums.length; i++){
if(path.size() > 0 && path.get(path.size() - 1) > nums[i]) continue;
if(flag == nums[i]) continue;
path.add(nums[i]);
backTracking(nums, i + 1);
flag = path.get(path.size() - 1);
path.remove(path.size() - 1);
}
}
}
改进后的代码依旧存在问题,去重依旧不彻底,如下实例:
解答失败:
测试用例:[1,2,3,4,5,6,7,8,9,10,1,1,1,1,1]
测试结果:[[1,2],[1,2,3],.....,[1,10],[1,1],[1,1,1],[1,1,1,1],[1,1,1,1,1],[1,1,1,1,1,1],......,[9,10],[1,1],[1,1,1],[1,1,1,1],[1,1,1,1,1]]
期望结果:[[1,2],[1,2,3],.....,[1,10],[1,1],[1,1,1],[1,1,1,1],[1,1,1,1,1],[1,1,1,1,1,1],.....,[9,10]]
stdout:
这个解法并不能将这种非同根的重复解去除。例如[1,1] [1,1,1]这种解,一个是由第一个1给出的,一个是由后面的第一个1给出的。
最终解决方案:
使用set去去重,一层一个set
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backTracking(nums, 0);
return ans;
}
public void backTracking(int[] nums, int startIndex){
if(path.size() >= 2) ans.add(new ArrayList<>(path));
Set<Integer> record = new HashSet<>();
for(int i = startIndex; i < nums.length; i++){
if(path.size() > 0 && path.get(path.size() - 1) > nums[i]) continue;
if(record.contains(nums[i])) continue;
record.add(nums[i]);
path.add(nums[i]);
backTracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
46.全排列
解题思路:
该题目只需要注意nums中的元素只能使用一次所以需要使用used去重,没有其他需要注意的点
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new LinkedList<>();
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
used = new boolean[nums.length];
Arrays.fill(used, false);
backTracking(nums, used);
return ans;
}
public void backTracking(int[] nums, boolean[] used){
if(path.size() == nums.length){
ans.add(new ArrayList<>(path));
return;
}
for(int i = 0; i < nums.length; i++){
if(used[i] == true) continue;
path.add(nums[i]);
used[i] = true;
backTracking(nums,used);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
47.全排列II
题目链接:47. 全排列 II - 力扣(LeetCode)
解题思路:
该题目和全排列的区别就在于存在了重复元素,所以需要在横向循环遍历的时候进行剪枝去重操作,去重方法就是使用set去重。 同样也需要使用used数组记录哪个元素使用过。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new LinkedList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
Arrays.fill(used, false);
backTracking(nums);
return ans;
}
public void backTracking(int[] nums){
if(path.size() == nums.length){
ans.add(new ArrayList<>(path));
return;
}
// 横向遍历剪枝
Set<Integer> record = new HashSet<>();
for(int i = 0; i < nums.length; i++){
if(used[i] == true) continue;
if(record.contains(nums[i])) continue;
record.add(nums[i]);
path.add(nums[i]);
used[i] = true;
backTracking(nums);
used[i] = false;
path.remove(path.size() - 1);
}
}
}