[英]LeetCode 40 time limit exceeded
我正在研究 LeetCode 40。组合总和 II
给定一组候选编号(candidates)和一个目标编号(target),找出候选编号总和为 target 的所有唯一组合。
候选中的每个数字在组合中只能使用一次。
注意:解决方案集不得包含重复的组合。
我下面的代码一直有效,直到候选人集合变得太大,然后我得到“超出时间限制”。 我认为所有的递归都创建了太多的循环。 我显然可以复制教程来获得答案,但我试图弄清楚我的代码如何更新才能工作,以便更好地理解递归是如何工作的。
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
answers = []
strings = []
total = sum(candidates)
if total < target:
return []
def backtrack(total, array, index):
if total == 0:
string = ''
for each in array:
string += str(each)
if string not in strings:
answers.append(array)
strings.append(string)
return
if index >= len(candidates):
return
for each in range(index, len(candidates)):
backtrack(total - candidates[each], array + [candidates[each]], each + 1)
backtrack(total, array, each + 1)
backtrack(target, [], 0)
return answers
对候选项进行排序是个好主意,但您不要使用它。 仅当 total-candidate[i]>0 时才应撤回,如果 <0 则中断,如果 =0 则返回
一些影响性能的问题:
当total
变为负时,继续递归过程是没有用的:它只会变得更负。
对backtrack
的第二个调用是实现与for
循环相同的想法。 考虑在循环中, each + 1
的所有可能值都将传递给backtrack(total, array, each + 1)
。 但还要注意,在递归树中更深一层,所有这些——除了第一个——都被重新制作了! 所以要么删除backtrack(total, array, each + 1)
要么保留它,然后删除for
循环。
当两个连续的值相同,并且已经对这两个中的第一个进行了递归调用时,使用重复值进行递归调用是没有用的。 在那一刻,可供选择的值少了一个,而且总数是一样的,所以不能从中产生任何新的组合。 因此,如果我们将 go 用于for
循环(请参阅前一点),那么只对不同的值进行递归调用(它们第一次出现)。
用于查找重复结果的字符串连接适用于测试用例,但它有点棘手,因为数字被连接在一起。 例如,当1, 2, 3
被字符串化时,它变为“123”。 但是 1, 23 也会像这样被字符串化,这可能会导致假阴性。 所以这实际上是 LeetCode 上未被检测到的代码中的错误。 您可以通过使用分隔符来解决此问题。
这是考虑到这些要点的代码:
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
answers = []
strings = []
total = sum(candidates)
if total < target:
return []
def backtrack(total, array, index):
if total == 0:
string = ''
for each in array:
string += "_" + str(each) # Separate the values
if string not in strings:
answers.append(array)
strings.append(string)
return
if index >= len(candidates) or total < 0: # give up when total becomes negative
return
current = -1
for each in range(index, len(candidates)):
if candidates[each] != current: # Avoid duplicates
current = candidates[each]
backtrack(total - current, array + [current], each + 1)
# Don't make another recursive call here. The for-loop is already skipping candidates
backtrack(target, [], 0)
return answers
仍有改进的余地。 你可以想到以下几点:
您的代码当前检查总数不大于总和。 您可以扩展它,并在递归过程中验证总数不大于剩余总和。 在开始递归之前,您将准备一个包含所有这些“剩余”总和的列表,然后根据该列表中的相关值检查总数。
if string not in strings
在strings
是列表时效率不高。 最好为strings
使用一个集合。
您可以创建元组而不是子列表,而不是使用字符串作为标识符。 这些是可散列的,所以如果你将它们存储在一个整体集合而不是列表中,你将不会得到重复。 然后在最后,您可以将该组元组转换为最终的列表列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.