前端重拾算法数据结构一个月(13)

110 阅读3分钟

第十三天

昨天做了力扣第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同个地址的问题也得到了解决,只要作为参数传就可以了。