leetcode-zgd-day29-491.递增子序列/46.全排列/47.全排列II

46 阅读2分钟

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.全排列

题目链接:46. 全排列 - 力扣(LeetCode)

解题思路:

该题目只需要注意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);
         }
     }
 }