【问题标题】:LeetCode 40 Combination Sum II time limit exceededLeetCode 40 Combination Sum II 超过时间限制
【发布时间】:2022-01-29 05:06:41
【问题描述】:

我正在研究 LeetCode 40. Combination Sum 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

【问题讨论】:

    标签: python algorithm recursion big-o backtracking


    【解决方案1】:

    影响性能的一些问题:

    • total变为负数时,继续递归过程是没有用的:它只会变得更负数。

    • backtrack 的第二次调用实现了与for 循环相同的想法。考虑在循环中each + 1 的所有可能值都将传递给backtrack(total, array, each + 1)。但也请注意,在递归树中更深一层,所有这些——除了第一个——都再次!所以要么删除backtrack(total, array, each + 1),要么保留它,然后删除for循环。

    • 当两个连续的值相同,并且已经对这两个中的第一个进行了递归调用时,用重复值进行递归调用是没有用的。在那一刻,可供选择的值少了一个,而且总数是一样的,所以不能从中产生任何新的组合。因此,如果我们使用 for 循环(参见上一点),那么只对不同的值进行递归调用(它们第一次出现时)。

    还有这个bug:

    • 用于查找重复结果的字符串连接适用于测试用例,但它有点笨拙,因为数字会粘在一起。例如,当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
    

    还有改进的余地。你可以想到以下几点:

    • 您的代码当前检查总数不大于总和。您可以扩展它,并在递归过程中验证总数不大于 remaining 总和。在开始递归之前,您将准备一个包含所有这些“剩余”总和的列表,然后根据该列表中的相关值检查总和。

    • if string not in stringsstrings 是一个列表时效率不高。最好使用strings的集合。

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

    【讨论】:

      【解决方案2】:

      对候选者进行排序是个好主意,但你不要使用它。仅当 total-candidate[i]>0 时才应撤回,如果 则返回

      【讨论】:

      • 这大大加快了速度,但仍然给更大的候选人集带来了问题
      猜你喜欢
      • 2022-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      • 2022-07-16
      • 1970-01-01
      • 2022-08-24
      相关资源
      最近更新 更多