Leetcode 39.组合总和

题目:39.组合总和

初始思路

和77.组合的区别在于,这里的元素可以重复使用,所以递归退出的条件就不能是个数。只能通过sum和target的关系判断。

代码

  1. var combinationSum = function (candidates, target) {
  2. let res = []
  3. let path = []
  4. // 从小到大排序
  5. candidates.sort((a, b) => a - b)
  6. const backtracking = (startIndex, sum) => {
  7. if (sum === target) {
  8. res.push([...path])
  9. return
  10. }
  11. for (let i = startIndex; i < candidates.length; i++){
  12. const n = candidates[i]
  13. // 如果和大于target就终止遍历
  14. if (n + sum > target) break
  15. path.push(n)
  16. sum += n
  17. backtracking(i, sum)
  18. // 回溯
  19. path.pop()
  20. sum -= n
  21. }
  22. }
  23. backtracking(0, 0)
  24. return res
  25. };

感想

  1. 图示
    image.png
  2. 主要要看懂回溯操作,相比较77更改了一些条件。有startIndex就会排除已经选取过的元素。

Leetcode 40.组合总和 II

题目:40.组合总和 II

初始思路

跟上一题比较,这题每个数字只能重复一次,但是给的数组里面元素是有重复的。也就是说每个解的内部是可以由重复元素的(次数最多和原数组一样)

代码

  1. var combinationSum2 = function (candidates, target) {
  2. let res = []
  3. let path = []
  4. let sum = 0
  5. const len = candidates.length
  6. // 从小到大排序
  7. candidates.sort((a, b) => a - b)
  8. // 标记数组
  9. let used = new Array(len).fill(false)
  10. const backtracking = (startIndex) => {
  11. if (sum === target) {
  12. res.push([...path])
  13. return
  14. }
  15. for (let i = startIndex; i < len && sum < target; i++){
  16. const n = candidates[i]
  17. // 如果n === candidates[i - 1]说明和前一个元素重复了,检查一下used数组,如果是false说明同一层candidates[i-1]使用过
  18. if (n + sum > target || (i > 0 && n === candidates[i - 1] && !used[i - 1])) continue
  19. path.push(n)
  20. sum += n
  21. used[i] = true
  22. backtracking(i + 1)
  23. // 回溯
  24. path.pop()
  25. sum -= n
  26. used[i] = false
  27. }
  28. }
  29. backtracking(0)
  30. return res
  31. };
  1. var combinationSum2 = function (candidates, target) {
  2. let res = []
  3. let path = []
  4. let sum = 0
  5. const len = candidates.length
  6. // 从小到大排序
  7. candidates.sort((a, b) => a - b)
  8. // 标记数组
  9. const backtracking = (startIndex) => {
  10. if (sum === target) {
  11. res.push([...path])
  12. return
  13. }
  14. for (let i = startIndex; i < len; i++) {
  15. const n = candidates[i]
  16. if (i > startIndex && candidates[i] === candidates[i - 1]) {
  17. //若当前元素和前一个元素相等
  18. //则本次循环结束,防止出现重复组合
  19. continue
  20. }
  21. //如果当前元素值大于目标值-总和的值
  22. //由于数组已排序,那么该元素之后的元素必定不满足条件
  23. //直接终止当前层的递归
  24. if (n + sum > target) break
  25. path.push(n)
  26. sum += n
  27. backtracking(i + 1)
  28. // 回溯
  29. path.pop()
  30. sum -= n
  31. }
  32. }
  33. backtracking(0)
  34. return res
  35. };

感想

  1. 回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。
  2. 所以我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。
  3. 图示
    image.png

Leetcode 131.分割回文串

题目:131.分割回文串

初始思路

回文字符串用双指针判断,一前一后往中间夹。

代码

  1. const isPalindrome = (str, left, right) => {
  2. for (let i = left, j = right; i < j; i++, j--) {
  3. if (str[i] !== str[j]) return false
  4. }
  5. return true
  6. }
  7. var partition = function (s) {
  8. let res = []
  9. let path = []
  10. const len = s.length
  11. const backtracking = (startIndex) => {
  12. if (startIndex >= len) {
  13. res.push([...path])
  14. return
  15. }
  16. for (let i = startIndex; i < len; i++) {
  17. if (!isPalindrome(s, startIndex, i)) continue
  18. // 截取这段字符串,注意左闭右开
  19. path.push(s.slice(startIndex, i + 1))
  20. // 因为不能重复,所以+1
  21. backtracking(i + 1)
  22. // 回溯
  23. path.pop()
  24. }
  25. }
  26. backtracking(0)
  27. return res
  28. };

感想

  1. 具体来说, 给定一个字符串s, 长度为n, 它成为回文字串的充分必要条件是s[0] == s[n-1]且s[1:n-1]是回文字串。
  2. 好难!
    image.png