第十一天
学习开始前,先来写一道题目吧,一样是力扣的第78题。
第二十二题
LeetCode78.子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入: nums = [1,2,3]
输出: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
思路: 这一题其实是组合问题的一个简化版(自我感觉)。但有一点要注意的是,它将满足条件的path推进result的场合并不是在终止条件里面,因为这个题目中没有k的限制,没有规定path中的元素个数,这代表着终止条件应该是path中的最后一个元素=nums中的最后一个元素(即每个都要进来)。
function subsets(nums: number[]): number[][] {
const path:number[] = []
const result:number[][] = []
const backtracking = (startIndex:number)=>{
result.push(path.map(i=>i))
if (path[path.length - 1] === nums[nums.length - 1]) return
for (let i = startIndex; i < nums.length; i++) {
path.push(nums[i])
backtracking(i + 1)
path.pop()
}
}
backtracking(0)
return result
};
一次写出!没有失误非常开心哈哈。那么今天来学习的回溯算法的内容是:组合总和。
组合总和
组合总和和组合有什么区别呢,这里用力扣的第39题作为模板来学习这个内容:
第二十三题
LeetCode39.组合总和
给你一个无重复元素的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以无限制重复被选取。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
思路: 还是根据我们的三部曲来做,第一步确认递归函数的参数以及返回值。对我来说让我先做第二步确认终止条件更好确认一些,那么这道题到什么程度需要返回呢,自然是在path中的元素相加等于target的时候了,于是终止条件就出来了
eval(path.join("+"))===target //eval()能计算 JavaScript 字符串,并把它作为脚本代码来执行。
但是!这是我之前犯的错误,不要使用eval()!!eval() 是一个危险的函数, 它使用与调用者相同的权限执行代码。eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。
后面我选择的方法是:
path.length > 0 && path.reduce((a, b) => a + b) === target
//这里要判断长度大于0,否侧reduce不生效
然后在题目中是这样,分别从2、3、6、7为startIndex跟之后的集合组成元素组合。所以我确认了一个参数为startIndex。
function combinationSum(candidates: number[ ], target: number): number[ ][ ] {
const path: number[ ] = [ ]
const result: number[ ][ ] = [ ]
const backtracking = (startIndex: number) => {
if (path.length > 0 && path.reduce((a, b) => a + b) === target) {
result.push(path.map((i) => i))
return
}
for (let i = startIndex; i < candidates.length; i++) {
path.push(candidates[i])
if (path.reduce((a, b) => a + b) > target) {
//这里剪枝,条件不满足就直接不用进入下一个递归了
path.pop()
continue
}
backtracking(i)
path.pop()
}
}
backtracking(0)
return result
};
在看一个导师的讲解中,他使用了一个sum作为path中元素的和,如果这样我就不用每次都使用reduce了。但我不太确定会节省很多的时间。反正空间肯定是会多用一丢丢,但是没差我试了一下,大概差了几ms,应该只是网络问题吧。
function combinationSum(candidates: number[], target: number): number[][] {
const path: number[] = []
const result: number[][] = []
const backtracking = (startIndex: number,sum:number) => {
if (sum === target) {
result.push(path.map((i) => i))
return
}
for (let i = startIndex; i < candidates.length; i++) {
path.push(candidates[i])
sum += candidates[i]
if (sum > target) {
path.pop()
sum -= candidates[i]
continue
}
backtracking(i,sum)
path.pop()
sum -= candidates[i]
}
}
backtracking(0,0)
return result
};
ok!今天到这里。明天周六还得上班QAQ