思路:组合的本质就是分组但不排序,所以可以从前往后看,例如[1,2,3,4] 分成2个一组
[1, 2] 、[1,3]、[1,4]、[2,3]、[2,4]、[3,4];1,2,3,4 可以看成是树的宽度,而树的深度就是k,因为要分成k个一组。递归的终止条件是,当self.path 的长度到达k时,就代表找到了一个结果,并将其加入到最终结果集中。而在递归的过程中,我们需要一个startIndex来帮助我们找到此时到了树的那一列宽度上,例如startIndex = 1时,也就是找到了[1,2]、[1,3]、[1,4]的前置1。
未剪枝
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
self.result = []
self.path = []
startIndex = 1
def backTracking(n, k, startIndex):
if len(self.path) == k:
self.result.append(self.path[:])
return
for i in range(startIndex, n+1):#1开始,到n为止
self.path.append(i)
backTracking(n, k, i + 1)
self.path.pop()
backTracking(n, k, startIndex)
return self.result
剪枝思路:
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
self.result = []
self.path = []
startIndex = 1
def backTracking(n, k, startIndex):
if len(self.path) == k:
self.result.append(self.path[:])
return
for i in range(startIndex, n - (k - len(self.path)) + 2):#1开始,到n为止 #这里可以进行剪枝优化
# 例如遍历到4时后面没有其它节点了,换句话说也就是起始位置之后的元素不足我们需要的元素个数了
# 那么实际上我们只要遍历 1 2 3就可以了, 我们需要k个,此时已经有len(self.path)个了,还需要k-len(self.path)个,那么往前推,1,2,3一直到n-(k-len(path))+1都是满足的起始下标,而python循环还要+1
self.path.append(i)
backTracking(n, k, i + 1)
self.path.pop()
backTracking(n, k, startIndex)
return self.result
题目链接:216. 组合总和 III - 力扣(LeetCode)
思路:跟上一题思路一样,主要是剪枝方式的不同;当当前path中sum的值已经大于n了,那直接返回
未剪枝
class Solution(object):
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
self.result = []
self.path = []
startIndex = 1
def backTracking(k, n, startIndex):
if len(self.path) == k and sum(self.path) != n:
return
if len(self.path) == k and sum(self.path) == n:
self.result.append(self.path[:])
return
for i in range(startIndex, 10):
self.path.append(i)
backTracking(k, n, i + 1)
self.path.pop()
backTracking(k, n, startIndex)
return self.result
剪枝:
class Solution(object):
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
self.result = []
self.path = []
startIndex = 1
def backTracking(k, n, startIndex):
if len(self.path) == k and sum(self.path) != n:
return
if len(self.path) == k and sum(self.path) == n:
self.result.append(self.path[:])
return
for i in range(startIndex, 9 - (k -len(self.path)) + 1 + 1):
print(i)
self.path.append(i)
if sum(self.path) > n:
self.path.pop()
return
backTracking(k, n, i + 1)
self.path.pop()
backTracking(k, n, startIndex)
return self.result
题目链接:17. 电话号码的字母组合 - 力扣(LeetCode)
思路:建立数字到字符集合的map映射,然后将数字字符逐个提出,转换成下标,并在map映射中找到对应的集合,然后现在第一个集合中遍历,并递归第二个/第三个集合
class Solution(object):
def __init__(self):
self.letterMap = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
]
self.result = []
self.s = ""
def backTracking(self, digits, index):
if index == len(digits):
self.result.append(self.s)
return
digit = int(digits[index]) #str->int
letters = self.letterMap[digit] #找到对应的字符集合
for i in range(len(letters)):
self.s += letters[i]
self.backTracking(digits, index + 1)
self.s = self.s[:-1]
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if len(digits) == 0:
return self.result
self.backTracking(digits, 0)
return self.result