繁体   English   中英

超过 LeetCode 40 时间限制

[英]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 stringsstrings是列表时效率不高。 最好为strings使用一个集合。

  • 您可以创建元组而不是子列表,而不是使用字符串作为标识符。 这些是可散列的,所以如果你将它们存储在一个整体集合而不是列表中,你将不会得到重复。 然后在最后,您可以将该组元组转换为最终的列表列表。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM