代码随想录算法训练营第二十八天 | 491. 递增子序列、46. 全排列、47. 全排列 II

31 阅读3分钟

491. 递增子序列

链接

文章链接

题目链接

第一想法

首先想到的还是用startIndex来防止一个数字用多次,之后想用startIndex用来去重,结果发现无法sort,所以想法是不对的,因为是要在树层去重就可以,所以想到了set,代码如下:

function findSubsequences(nums: number[]): number[][] {
    let res:number[][]=[]
    const foo=(arr:number[],startIndex:number,nums:number[])=>{
      if(arr.length>=2){
          res.push(Array.from(arr))
      }
      let set:Set<number>=new Set()//用于去重
      for(let i=startIndex;i<nums.length;i++){
          if(set.has(nums[i]))continue//去重
          if(arr.length>0&&arr[arr.length-1]>nums[i]) continue//保证递增
          set.add(nums[i])
          arr.push(nums[i])
          foo(arr,i+1,nums)
          arr.pop()
      }
    }
    foo([],0,nums)
    return res
};

看完文章后的想法

文章也是和我用一样的想法,由于nums[i]有范围限制,所以我用的是set,文章用的是array数组,都是可以的,对于我上面写的大家如果不明白可以看这张图:

image.png 这张图很清晰的说明了是本层而不是树枝去重的原因。

思考

这道题和子集问题很像,但是其中却有着不一样的思想,因为子集是可以排序的,但是这道题是不允许排序的,所以以前去重的方法是行不通的,所以只能用set进行去重,这也是这道题的核心考点。

46. 全排列

链接

文章链接

题目链接

第一想法

这道题每个数字也是只能用一次的,但是这回我觉得不能用startIndex,因为全排列[1,2]和[2,1]是不同的两个全排列,所以我觉得不能用startIndex,所以这道题其实是树枝去重,树枝上的元素不能重复使用,所以我就想到了用set

function permute(nums: number[]): number[][] {
    let res:number[][]=[]
     let set:Set<number>=new Set()
 const foo=(arr:number[],set:Set<number>)=>{
     if(arr.length==nums.length){
         res.push(arr.slice())
         return
     }
     for(let i=0;i<nums.length;i++){
         if(set.has(nums[i])) continue//去重
         arr.push(nums[i])
         set.add(nums[i])//往下层递归走时需要去重
         foo(arr,set)
         arr.pop()
         set.delete(nums[i])//回溯的时候是要把set去除当前的nums,防止影响到树层的遍历
     }
 }
 foo([],set)
 return res
};

看完文章后的想法

文章的思路和我是一样的,不一样的是去重方法,我用的是set,而文章用的是used数组进行去重的,就如这张图:

image.png 代码如下:

function permute(nums: number[]): number[][] {
    let res:number[][]=[]
    let used:boolean[]=new Array(nums.length).fill(false) //用于记录这条树枝上这个元素是否已经使用过了
 const foo=(arr:number[],used:boolean[])=>{
     if(arr.length==nums.length){
         res.push(arr.slice())
         return
     }
     for(let i=0;i<nums.length;i++){
         if(used[i]) continue //为true说明已经使用过了
         arr.push(nums[i])
         used[i]=true//标记已经使用过来
         foo(arr,used)
         arr.pop()
         used[i]=false //回溯
     }
 }
 foo([],used)
 return res
};

思考

这道题是要树枝去重,所以用startIndex就不合适了,可以用两种方法进行树枝去重,一种就是我那种set进行去重,另一种就是文章所写的去重方法,使用used数组来判断哪个已经使用而哪个未使用

47. 全排列 II

链接

文章链接

题目链接

第一想法

这道题由于数字是可以重复的,所以不止要横向去重(树层去重),也要纵向去重(树枝去重),这里的纵向去重是去除重复使用的,代码如下:

function permuteUnique(nums: number[]): number[][] {
    let res:number[][]=[]
    let used:boolean[]=new Array(nums.length).fill(false)//横向去重
    const foo=(arr:number[],used:boolean[],nums:number[])=>{
        if(arr.length==nums.length){
            res.push(arr.slice())
            return
        }
        let set:Set<number>=new Set()//用于纵向去重
        for(let i=0;i<nums.length;i++){
            if(used[i]||set.has(nums[i]))continue
            used[i]=true
            set.add(nums[i])
            arr.push(nums[i])
            foo(arr,used,nums)
            used[i]=false
            arr.pop()
        }
    }
    foo([],used,nums)
    return res
};

看完文章后的想法

文章也是树枝去重(重复使用)和树层去重,不一样的是文章只用了一个used就解决了这个问题,因为可以通过used[i]==used[i-1]&&used[i-1]==false来进行横向去重的(树层去重),代码如下:

function permuteUnique(nums: number[]): number[][] {
    let res:number[][]=[]
    let used:boolean[]=new Array(nums.length).fill(false)
    nums.sort((a,b)=>a-b)
    const foo=(arr:number[],used:boolean[],nums:number[])=>{
        if(arr.length==nums.length){
            res.push(arr.slice())
            return
        }
        for(let i=0;i<nums.length;i++){
            if(i>0&&nums[i-1]==nums[i]&&used[i-1]==false) continue //树层去重
            if(used[i])continue
            used[i]=true
            arr.push(nums[i])
            foo(arr,used,nums)
            used[i]=false
            arr.pop()
        }
    }
    foo([],used,nums)
    return res
};

如果把i>0&&nums[i-1]==nums[i]&&used[i-1]==false换成i>0&&nums[i-1]==nums[i]&&used[i-1]==true也是可以的,唯一的区别是应该是在树层就去重了,一个是树枝去重。 树层去重(used[i-1]==false):

image.png 树枝去重(used[i-1]==true):

image.png

思考

这道题需要考虑两方面,一个是树枝上不能重复使用数字,第二个是树层上要去重,我的想法是用used树枝去重,set树层去重。文章的方法更好,通过used数组就完成了树枝去重和树层去重两个方面。这道题其实只用used是挺有难度的

今日总结

今天耗时2.5小时,三道题都挺有难度,但是通过set都可以做出来,虽然可能不是最好的方法,但是靠自己都做出来了,文章的方法更加巧妙,也是我应该学习的,used数组技能树枝去除使用重复的数字,也能树层去重。