LeetCode组合问题汇总

LeetCode组合问题汇总

文章目录

    • LeetCode组合问题汇总
      • 77. 组合
        • 解法1 递归 + 回溯
        • 解法1-1 减枝优化
      • 39. 组合总和
        • 解法1 递归+ 回溯
        • 解法1-1 递归 + 回溯
      • 40. 组合总和 II
        • 解法1 递归 + 回溯
      • 216. 组合总和 III
        • 解法1 递归 + 回溯
        • 解法1-1 减枝处理
      • 377. 组合总和 Ⅳ
        • 解法1 递归+ 回溯
        • 解法1-1 记忆 递归
        • 解法2 动态规划
      • 总结

​ 今天来总结一下 LeetCode 中 的组合问题, 下面搜索leetcode 题目里面的组合 问题,有5道题 左右,今天简单总结一下。题号是 39 ,40 ,216,377, 77

77. 组合

https://leetcode-cn.com/problems/combinations/

难度中等490收藏分享切换为英文接收动态反馈

给定两个整数 nk,返回 1 … n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

解法1 递归 + 回溯

关于递归 可以参考一下之前的文章。程序员的自我修养-算法递归

组合问题 很多情况,都可以使用递归来解决。

from typing import List


class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:

        self.result = []
        start = 1
        level = 0
        cur_result = []
        self._generate(n, k, start=start, level=level, cur_result=cur_result)
        return self.result
        pass

    def _generate(self, n: int, k: int, start: int, level: int, cur_result: List[int]):
        # terminator
        if level == k:
            self.result.append(cur_result.copy())
            return

        # current level logic
        for i in range(start, n + 1):
            cur_result.append(i)
            self._generate(n, k, i + 1, level + 1, cur_result)
            # reverse the current level status
            cur_result.pop()


if __name__ == '__main__':
    r = Solution().combine(n=4, k=2)
    print(r)

解法1-1 减枝优化

如果不满足条件 可以直接减枝处理,而不是 蛮力递归下去。 减枝条件 就是

k - level 当前还需几个数字 , n - (k-level) +1 代表 起始位置, 为啥要加一, 要包含起点所以加一, 这个调试 代码 确定边界 .

from typing import List

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:

        self.result = []
        start = 1
        level = 0
        cur_result = []
        self._generate(n, k, start=start, level=level, cur_result=cur_result)
        return self.result
        pass

    def _generate(self, n: int, k: int, start: int, level: int, cur_result: List[int]):
        # terminator
        if level == k:
            self.result.append(cur_result.copy())
            return

        # current level logic
        # 减枝处理 end = n - (k - level) + 1
        for i in range(start, n - (k - level) + 1 + 1):
            cur_result.append(i)
            self._generate(n, k, i + 1, level + 1, cur_result)
            # reverse the current level status
            cur_result.pop()

39. 组合总和

https://leetcode-cn.com/problems/combination-sum/

难度中等1157收藏分享切换为英文接收动态反馈

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

解法1 递归+ 回溯

candidates 注意这个数组 是不包含重复元素的。

from typing import List


class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:

        self.result = []

        self.length = len(candidates)
        cur_result = []
        start = 0
        self._generate(candidates, target, start, cur_result)
        return self.result

    def _generate(self, candidates: List[int], target: int, start: int, cur_result: List):

        if sum(cur_result) > target:
            return

        if sum(cur_result) == target:
            self.result.append(cur_result.copy())
            return

        for i in range(start, self.length):
            cur_result.append(candidates[i])

            self._generate(candidates, target, i, cur_result)
            cur_result.pop(-1)

        pass

解法1-1 递归 + 回溯

改变 target值,每次选一个 数组后, target= target - candidates[i] 这样 递归的终止条件 就可以 和 零 进行比较, 如果小于0 没有结果直接返回, 等于零 收集结果然后 返回即可。 这里 题目中要求 candidates 都是正整数 所以可以这样做,如果有负数 就比较麻烦一点。


class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:

        self.result = []
        self.length = len(candidates)
        cur_result = []
        start = 0
        self._generate(candidates, target, start, cur_result)
        return self.result
        pass

    def _generate(self, candidates: List[int], target: int, start: int, cur_result: List):

        if 0 > target:
            return

        if 0 == target:
            self.result.append(cur_result.copy())
            return

        for i in range(start, self.length):
            cur_result.append(candidates[i])

            self._generate(candidates, target - candidates[i], i, cur_result)
            cur_result.pop(-1)

        pass

40. 组合总和 II

https://leetcode-cn.com/problems/combination-sum-ii/

难度中等487收藏分享切换为英文接收动态反馈

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]

解法1 递归 + 回溯

关键如何去重逻辑

from typing import List


class Solution:

    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:

        candidates.sort()
        self.result = []

        self.length = len(candidates)
        self.visited = [False] * self.length

        start = 0
        cur_result = []

        self._generate(candidates, target, start, cur_result)

        return self.result

    def _generate(self, candidates: List[int], target: int, start, cur_result: List):

        if sum(cur_result) > target:
            return
        if sum(cur_result) == target:
            self.result.append(cur_result.copy())
            return

        for i in range(start, self.length):

            if i > 0 and candidates[i] == candidates[i - 1] and not self.visited[i - 1]:
                continue

            cur_result.append(candidates[i])
            self.visited[i] = True
            self._generate(candidates, target, i + 1, cur_result)

            cur_result.pop(-1)
            self.visited[i] = False

        pass

216. 组合总和 III

https://leetcode-cn.com/problems/combination-sum-iii/

难度中等258收藏分享切换为英文接收动态反馈

找出所有相加之和为 nk 个数的组合***。***组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

解法1 递归 + 回溯

from typing import List


class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        self.result = []
        level = 0
        cur_result = []
        start = 1
        self._generate(k, n, level, start, cur_result)
        return self.result
        pass

    def _generate(self, k: int, n: int, level: int, start: int, cur_result: List):

        if level == k:
            if sum(cur_result) == n:
                self.result.append(cur_result.copy())
            return

        for i in range(start, 10):
            cur_result.append(i)
            self._generate(k, n, level + 1, i + 1, cur_result)

            cur_result.pop(-1)


解法1-1 减枝处理

减枝处理 减少一些不必要的递归。

from typing import List


class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        self.result = []
        level = 0
        cur_result = []
        start = 1
        self._generate(k, n, level, start, cur_result)
        return self.result
        
    def _generate(self, k: int, n: int, level: int, start: int, cur_result: List):

        if level == k:
            if sum(cur_result) == n:
                self.result.append(cur_result.copy())
            return

        for i in range(start, 9 - (k -level) +1+1):
            cur_result.append(i)
            self._generate(k, n, level + 1, i + 1, cur_result)

            cur_result.pop(-1)

377. 组合总和 Ⅳ

https://leetcode-cn.com/problems/combination-sum-iv/

难度中等279收藏分享切换为英文接收动态反馈

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

示例:

nums = [1, 2, 3]
target = 4

所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

请注意,顺序不同的序列被视作不同的组合。

因此输出为 7。

进阶:
如果给定的数组中含有负数会怎么样?
问题会产生什么变化?
我们需要在题目中添加什么限制来允许负数的出现?

解法1 递归+ 回溯

提交之后 发现超时了。。

递归 + 回溯 本质上就是 暴力搜索, 这种时间复杂度 比较高。

还有这个题目 只是要求 求多少个 ,并没有要求 求出具体的组合数(其实排序数), 所以我们要想办法转成记忆话递归。

from typing import List


class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:

        self.result = []
        self.length = len(nums)
        cur_result = []
        self._generate(nums, target, cur_result)
        return len(self.result)
        pass

    def _generate(self, nums: List, target: int, cur_result: List):
        # terminator
        if sum(cur_result) > target:
            return
        if sum(cur_result) == target:
            self.result.append(cur_result.copy())
            return

        for i in range(0, self.length):
            cur_result.append(nums[i])
            self._generate(nums, target, cur_result)

            cur_result.pop(-1)


if __name__ == '__main__':
    nums = [1, 2, 3]
    target = 4

    res = Solution().combinationSum4(nums, target)

    print(res)
    # for r in res:
    #     print(r)
    pass

解法1-1 记忆 递归

把上面的代码 稍作修改 . 把这个问题想办法 转成一个子问题。

定义 _generate(nums,target) 组合的个数 ,意思是 从nums 随便拿出数字 组成的和 等于target 的个数

所以转成子问题 length = len(nums), 从nums 拿出num 与 target-num 这个子问题, 把 每个子问题的值 相加 即使结果 。

[

_generate( nums[0] , target-nums[0]) ,

_generate( nums[1] , target-nums[1]) ,

_generate( nums[2] , target-nums[2]) ,

_generate( nums[length-1] , target-nums[length-1]) ,

]

为了 使用记忆化递归, 我们使用一个字典保存一些中间结果。

self.memo[0] = 1 这里代表当 target=0 有一个结果。

from typing import List

from collections import defaultdict

            

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        self.length = len(nums)
        self.memo = defaultdict(int)
        self.memo[0] = 1
        r = self._generate(nums, target)
        return r
        pass

    def _generate(self, nums: List, target: int):
        # terminator
        if 0 > target:
            return 0

        if target in self.memo:
            # 收集结果
            # print(f"{target},val:{self.memo[target]}")
            return self.memo[target]

        total = 0
        for i in range(0, self.length):
            total += self._generate(nums, target - nums[i])

        self.memo[target] = total
        return total
            

解法2 动态规划

这个题目可以转化为 背包问题。 nums 数组可以看做物品, target 要装的重量。 每个物品有无限多个,求装满 这个背包所需要的方法数目

参考题解

https://leetcode-cn.com/problems/combination-sum-iv/solution/377-zu-he-zong-he-ivdong-tai-gui-hua-xia-kbo0/

from typing import List


class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0] * (target + 1)
        dp[0] = 1

        for i in range(1, target + 1):
            for num in nums:
                if i - num >= 0:
                    dp[i] += dp[i - num]
        return dp[target]

总结

​ 这里总结了 LeetCode 组合问题常见解法, 也算是一个笔记。方便以后可以阅读,学习。 如果有帮助到你,可以给我点赞哦。 如果你发现有问题也可以一起交流哦,可以直接留言。

分享快乐,留住感动. '2021-02-14 17:46:26' --frank

你可能感兴趣的:(算法与数据结构,组合,组合问题,LeetCode组合,算法,leetcode)