Bootstrap

【代码随想录训练营】【Day29】第七章|回溯算法|491.递增子序列|46.全排列|47.全排列 II

递增子序列

题目详细:LeetCode.491

注意这道题求的是子序列,而不是子数组,子数组要求其数组是原数组的子集,且元素是连续的,而子序列只需要保证至少有两个元素即可,不需要关系元素是否连续。

所以一开始我确定了递归的结束条件是 startIndex == nums.length 之后,在循环过程中增加了nums[i] - nums[i - 1] < 0 来判断序列是否递增,如果递增则直接break,这样的做法是错误的,因为我们要求的是所有的可能的子序列,所以不能够直接跳出循环,还需要取后续树上的节点。

同时nums[i] - nums[i - 1] < 0 ,判断的是在原数组中连续的元素是否递增,但是子序列并不要求元素是连续的,所以这一判断条件本身也是错的,应该改为nums[i] < path.getLast(),用当前数值来比较上一个递增序列的尾数来判断是否是递增子序列。

在求子序列的同时,还要注意去重,一开始由于收到前面练习题的启发,我利用布尔数组used来进行去重,但发现在这道题中并不适用,因为之前利用used去重,都需要先将原数组进行排序;但在这道题中,排序之后,就得不到与原数组一样的递增子序列,变成从头到尾分割递增序例了,这是互相矛盾的。

所以正确的去重方式,应该是在每一层循环中都新建一个哈希表,用于记录在树形结构的同一层中(循环过程中)已经出现过的数字,假如该数值已经被添加到某一个子序列中,那么后面则不能再添加到该序列中,否则会出现重复的子序列。

这里我们在每一次递归中都新建一个哈希Set来记录在本次循环中出现过的数字,如果出现过,则跳过该数字,继续往后寻找递增的数字组成递增子序列。

以上仅是我的解题思考过程,以及踩过的一些坑和后续看完题解后,对思路的纠正过程,详细的题解可查阅:《代码随想录》— 递增子序列

Java解法(递归,回溯):

class Solution {
   
    List<List<Integer>> ans = new ArrayList<>();
    Deque<Integer> path = new ArrayDeque<>();

    public void backTrack(int[] nums, int startIndex){
   
        if(path.size() > 1){
   
            ans.add(new ArrayList<>(path));
        }
        if(startIndex == nums.length){
   
            return;
        }
        Set<Integer> set = new HashSet<>();
        for(int i = startIndex; i < nums.length; i++){
   
            if((!path.isEmpty() && nums[i] < path.getLast()) || set.contains(nums[i])){
   
                continue;
            }
            set.add(nums[i]);
            path.offer
;