代码随想录算法Day29 | 491.递增子序列 ,46.全排列 ,47.全排列 II

 

491.递增子序列

题目链接:491. 递增子序列 - 力扣(LeetCode)

思路

这道题和 90.集合II 类似。不同的是这道题不能通过排序使相同元素聚集在一起,因此不能使用 used数组 或者 startIndex 的方法去重

在这道题中,虽然我们不可以让相同元素聚集在一起,但是我们可以通过 map 或 数组来当哈希(题目数值范围[-100,100])来记录当前

树层已经是否已经使用该元素。

注意,进入新一层的时候要重新定义(清空),map 或 数组当哈希 只负责记录本次是否已经访问过。

详细如图所示。

 

 

代码

 1 // 使用数组当map
 2 class Solution {
 3     private List<Integer> path = new ArrayList<>();
 4     private List<List<Integer>> res = new ArrayList<>();
 5     public List<List<Integer>> findSubsequences(int[] nums) {
 6         backtracking(nums,0);
 7         return res;
 8     }
 9 
10     private void backtracking (int[] nums, int start) {
11         if (path.size() > 1) {
12             res.add(new ArrayList<>(path));
13         }
14 
15         int[] used = new int[201];
16         for (int i = start; i < nums.length; i++) {
17             if (!path.isEmpty() && nums[i] < path.get(path.size() - 1) ||
18                     (used[nums[i] + 100] == 1)) continue;
19             used[nums[i] + 100] = 1;
20             path.add(nums[i]);
21             backtracking(nums, i + 1);
22             path.remove(path.size() - 1);
23         }
24     }
25 }
 1 //法二:使用map
 2 class Solution {
 3     //结果集合
 4     List<List<Integer>> res = new ArrayList<>();
 5     //路径集合
 6     LinkedList<Integer> path = new LinkedList<>();
 7     public List<List<Integer>> findSubsequences(int[] nums) {
 8         getSubsequences(nums,0);
 9         return res;
10     }
11     private void getSubsequences( int[] nums, int start ) {
12         if(path.size()>1 ){
13             res.add( new ArrayList<>(path) );
14             // 注意这里不要加return,要取树上的节点
15         }
16         HashMap<Integer,Integer> map = new HashMap<>();
17         for(int i=start ;i < nums.length ;i++){
18             if(!path.isEmpty() && nums[i]< path.getLast()){
19                 continue;
20             }
21             // 使用过了当前数字
22             if ( map.getOrDefault( nums[i],0 ) >=1 ){
23                 continue;
24             }
25             map.put(nums[i],map.getOrDefault( nums[i],0 )+1);
26             path.add( nums[i] );
27             getSubsequences( nums,i+1 );
28             path.removeLast();
29         }
30     }
31 }

 

46.全排列

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

思路

首先通过题意可知排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这和之前分析的子集以及组合所不同的地方。

因此每次循坏都要从零开始,而在遍历的时候就需要 “树枝去重”。

树枝去重关键点在于used数组的使用。在used数组中 我们把上层已经遍历过的元素 记为 true ,因此在遍历的时候

只要used数组为true,就直接进入到下一层。

具体过程如下

 

 

注意,回溯的时候也要把used一并回溯。

代码

 1 // 使用used数组
 2 class Solution {
 3 
 4     List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
 5     LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
 6     boolean[] used;
 7     public List<List<Integer>> permute(int[] nums) {
 8         if (nums.length == 0){
 9             return result;
10         }
11         used = new boolean[nums.length];
12         permuteHelper(nums);
13         return result;
14     }
15 
16     private void permuteHelper(int[] nums){
17         if (path.size() == nums.length){
18             result.add(new ArrayList<>(path));
19             return;
20         }
21         for (int i = 0; i < nums.length; i++){
22             if (used[i]){
23                 continue;
24             }
25             used[i] = true;
26             path.add(nums[i]);
27             permuteHelper(nums);
28             path.removeLast();
29             used[i] = false;
30         }
31     }
32 }
 1 // 解法2:通过判断path中是否存在数字,排除已经选择的数字
 2 class Solution {
 3     List<List<Integer>> result = new ArrayList<>();
 4     LinkedList<Integer> path = new LinkedList<>();
 5     public List<List<Integer>> permute(int[] nums) {
 6         if (nums.length == 0) return result;
 7         backtrack(nums, path);
 8         return result;
 9     }
10     public void backtrack(int[] nums, LinkedList<Integer> path) {
11         if (path.size() == nums.length) {
12             result.add(new ArrayList<>(path));
13         }
14         for (int i =0; i < nums.length; i++) {
15             // 如果path中已有,则跳过
16             if (path.contains(nums[i])) {
17                 continue;
18             } 
19             path.add(nums[i]);
20             backtrack(nums, path);
21             path.removeLast();
22         }
23     }
24 }

 

47.全排列 II

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

思路

这道题与上面一题大致,区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列。

因此这道题需要用到 “树层去重” 和 “树枝去重” 。

详细过程如下

 

 

注意:这里去重的时候一定要先对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了

代码

 1 class Solution {
 2     //存放结果
 3     List<List<Integer>> result = new ArrayList<>();
 4     //暂存结果
 5     List<Integer> path = new ArrayList<>();
 6 
 7     public List<List<Integer>> permuteUnique(int[] nums) {
 8         boolean[] used = new boolean[nums.length];
 9         Arrays.fill(used, false);
10         Arrays.sort(nums);
11         backTrack(nums, used);
12         return result;
13     }
14 
15     private void backTrack(int[] nums, boolean[] used) {
16         if (path.size() == nums.length) {
17             result.add(new ArrayList<>(path));
18             return;
19         }
20         for (int i = 0; i < nums.length; i++) {
21             // used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过
22             // used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过
23             // 如果同⼀树层nums[i - 1]使⽤过则直接跳过
24             if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
25                 continue;
26             }
27             //如果同⼀树⽀nums[i]没使⽤过开始处理
28             if (used[i] == false) {
29                 used[i] = true;//标记同⼀树⽀nums[i]使⽤过,防止同一树枝重复使用
30                 path.add(nums[i]);
31                 backTrack(nums, used);
32                 path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复
33                 used[i] = false;//回溯
34             }
35         }
36     }
37 }

扩展

这道题去重的关键在于

1 if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
2     continue;
3 }

如果改成 used[i - 1] == true, 也是正确的!,去重代码如下:

1 if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) {
2     continue;
3 }

第一个是对树层进行去重,而第二个代码是对树枝去重。

对于排列问题,树层去重 与 树枝去重 都是可行,区别在于 树层的效率会比较高一点。

详细差别如图:

树层去重

 

树枝去重

 

 

posted @ 2023-03-01 17:36  颜欢兮  阅读(19)  评论(0)    收藏  举报