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数组,都是可以的,对于我上面写的大家如果不明白可以看这张图:
这张图很清晰的说明了是本层而不是树枝去重的原因。
思考
这道题和子集问题很像,但是其中却有着不一样的思想,因为子集是可以排序的,但是这道题是不允许排序的,所以以前去重的方法是行不通的,所以只能用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数组进行去重的,就如这张图:
代码如下:
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):
树枝去重(used[i-1]==true):
思考
这道题需要考虑两方面,一个是树枝上不能重复使用数字,第二个是树层上要去重,我的想法是用used树枝去重,set树层去重。文章的方法更好,通过used数组就完成了树枝去重和树层去重两个方面。这道题其实只用used是挺有难度的
今日总结
今天耗时2.5小时,三道题都挺有难度,但是通过set都可以做出来,虽然可能不是最好的方法,但是靠自己都做出来了,文章的方法更加巧妙,也是我应该学习的,used数组技能树枝去除使用重复的数字,也能树层去重。