LeetCode组合问题汇总
文章目录
今天来总结一下 LeetCode 中 的组合问题, 下面搜索leetcode 题目里面的组合 问题,有5道题 左右,今天简单总结一下。题号是 39 ,40 ,216,377, 77
77. 组合
https://leetcode-cn.com/problems/combinations/
难度中等490收藏分享切换为英文接收动态反馈
给定两个整数 n 和 k,返回 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收藏分享切换为英文接收动态反馈
找出所有相加之和为 n 的 k 个数的组合***。***组合中只允许含有 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 组合问题常见解法, 也算是一个笔记。方便以后可以阅读,学习。 如果有帮助到你,可以给我点赞哦。 如果你发现有问题也可以一起交流哦,可以直接留言。