第十三天
昨天做了力扣第40题,做了很久很久做不出来,堆积太多思路了反而写不出来了,所以决定今天继续。
LeetCode40.组合总和 II
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 :
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
接下来先写下昨天的思路:(已经跳过了一些失败代码QAQ)
function combinationSum2(candidates: number[], target: number): number[][] {
const path: number[] = []
const result: number[][] = []
let hash = {}
const newCan = candidates.sort((a, b) => a - b)
const backtracking = (startIndex: number) => {
if (path.length > 0 && path.reduce((a, b) => a + b) === target) {
const newPath = path.map((i) => i)
if (!hash[newPath.join("-")]) {
hash[newPath.join("-")] = true
result.push(newPath)
}
return
}
for (let i = startIndex; i < newCan.length; i++) {
path.push(newCan[i])
if (path.reduce((a, b) => a + b) > target) {
path.pop()
return
}
backtracking(i + 1)
path.pop()
}
}
backtracking(0)
return result
};
用了哈希去重,因为二维数组不能使用includes来去重。先用newCan来排序,这样在for循环中的if判断只要大于target说明后面的数肯定都不行,所以就可以直接return。但 还是 超时了。
所以下面减少一次计算,反正在for里面的已经计算过了,要用来与target比较,所以现在就加一个参数,直接传到递归,这样就可以少计算一次。
function combinationSum2(candidates: number[], target: number): number[][] {
const path: number[] = []
const result: number[][] = []
let hash = {}
const newCan = candidates.sort((a, b) => a - b)
const backtracking = (startIndex: number, lastPath: number) => {
if (lastPath === target) {
const newPath = path.map((i) => i)
if (!hash[newPath.join("-")]) {
hash[newPath.join("-")] = true
result.push(newPath)
}
return
}
for (let i = startIndex; i < newCan.length; i++) {
path.push(newCan[i])
const nowPath = path.reduce((a, b) => a + b)
if (nowPath > target) {
path.pop()
return
}
backtracking(i + 1,nowPath)
path.pop()
}
}
backtracking(0,0)
return result
};
还是不行,有一个100个1的情况,所以加了一个专门解决这种问题的判断
function combinationSum2(candidates: number[], target: number): number[][] {
const path: number[] = []
const result: number[][] = []
const map: Map<string, number> = new Map()
const newCan = candidates.filter((i) => i <= target).sort((a, b) => a - b)
const ifOnly = Array.from(new Set(newCan))
if (ifOnly.length === 1) {
if ((target / ifOnly[0]) % 1 === 0&& newCan.length * ifOnly[0] >= target) {
result.push(new Array(target / ifOnly[0]).fill(ifOnly[0]))
return result
} else {
return result
}
}
const backtracking = (startIndex: number, lastPath: number) => {
if (lastPath === target) {
const newPath = path.map((i) => i)
const Str = JSON.stringify(newPath)
if (!map.has(Str)) {
map.set(Str, Math.random())
result.push(newPath)
}
return
}
for (let i = startIndex; i < newCan.length; i++) {
path.push(newCan[i])
const nowPath = path.reduce((a, b) => a + b)
if (nowPath > target) {
path.pop()
return
}
if (path.length === 1 && i > 0 && newCan[i] === newCan[i - 1]) {
path.pop()
continue
}
backtracking(i + 1,nowPath)
path.pop()
}
}
backtracking(0,0)
return result
};
结果结果结果QAQ还是不行,有一个100个1中间插了一个2的情况QAQ写了好几个小时了。 今天冷静下来,重新写,思路是对的了,只不过累计太多废代码,怎么写都是错的了: 三部曲,第一步:确定返回值、参数。startIndex,sum肯定是有的,因为有累计数量,有sum就不用每次重新遍历path加一遍了。然后今天在解析中有看到之前result.push(path)地址是同一个的解决方法,虽然它没明说但我注意到了,可以将path也作为参数传,这样每次也都是一个新的数了。所以确定了参数有三个。接着是第二步:终止条件,这个很简单就是sum===target。第三步:单层循环逻辑。首先是将每层循环新的数加入到sum中,此时就可以先判断一下sum是否大于target如果是直接将sum变回原来再continue就行了,不需要进行后续的操作了。如果不大于那么就可以将他推到参数中的path中并进行递归了,递归后就是回溯,常规操作。
function combinationSum2(candidates: number[], target: number): number[][] {
const result: number[][] = []
candidates = candidates.sort()
const backtracking = (startIndex: number, sum: number, path: number[]) => {
if (sum === target) {
result.push([...path])
return
}
for (let i = startIndex; i < candidates.length; i++) {
sum += candidates[i]
if(sum>target){
sum -= candidates[i]
continue
}
path.push(candidates[i])
backtracking(i + 1, sum, path)
sum -= candidates[i]
path.pop()
while (candidates[i + 1] === candidates[i]) {
i++
}
}
}
backtracking(0,0,[])
return result
};
结果写出来发现只要这么点就够了。之前path同个地址的问题也得到了解决,只要作为参数传就可以了。