现在你问「怎么剪枝」,其实就是在 循环里面提前判断,如果后面剩下的数字不够了,就不用再递归下去了。

发布于:2025-09-06 ⋅ 阅读:(17) ⋅ 点赞:(0)

好问题 👍,这行代码是剪枝的关键
我给你拆开解释,再举一个例子。


原来的写法

for i in range(start, len(nums)):

👉 意思是从 startlen(nums)-1 都试一遍。
这样写没问题,但是可能会进入没必要的递归


改进的写法

for i in range(start, len(nums) - (k - len(path)) + 1):

这里 len(nums) - (k - len(path)) + 1 是一个剪枝上界
我们来分解它:

  • len(nums):总共有多少个数

  • len(path):当前已经选了多少个数

  • k - len(path):还需要多少个数

  • len(nums) - (k - len(path)):保证后面至少能取到 k - len(path) 个数

  • +1:因为 range 的右边界是开区间,要多加 1


def simple_backtrack():
    nums = [1, 2, 3]
    k = 2
    result = []

    def backtrack(path, start):
        # 结束条件:长度为 k 就收集答案
        if len(path) == k:
            result.append(path[:])
            return

        # 剪枝: i 最大只能到 len(nums) - (k - len(path))
        for i in range(start, len(nums) - (k - len(path)) + 1):
            # 做选择
            path.append(nums[i])
            print(f"选择 {nums[i]},当前path: {path}")

            # 递归
            backtrack(path, i + 1)

            # 撤销选择
            path.pop()
            print(f"撤销 {nums[i]},当前path: {path}")

    backtrack([], 0)
    return result


# 运行
result = simple_backtrack()
print(f"最终结果: {result}")

---判断的意义。后面的全算上也不能满足组合的长度了————

👌 好的,我给你把 len(nums) - (k - len(path)) + 1 的意义和随 path 变化的情况拆开分析。


设:

  • n = len(nums)

  • k = 目标组合长度

  • len(path) = 当前已经选了多少个数

公式:

上界 = n - (k - len(path)) + 1

意思是:
我必须保证从当前位置 i 开始,后面剩下的数足够补齐 k - len(path) 个,否则没必要再选。


举例:n=5, k=3

nums = [1,2,3,4,5]


① 初始状态
  • path = []

  • len(path) = 0

  • 还需要 k - len(path) = 3

  • 上界 = 5 - 3 + 1 = 3

👉 for i in range(start, 3)
只能取 i = 0,1,2(对应数字 1,2,3)
不会取 i=3,4(因为只剩下 2 个数,不够 3 个了)。


② 当 path=[1]
  • len(path)=1

  • 还需要 k - len(path) = 2

  • 上界 = 5 - 2 + 1 = 4

👉 for i in range(start, 4)
可以取 i=1,2,3(对应数字 2,3,4)
不会取 i=4(因为只剩 5,不够 2 个了)。


③ 当 path=[1,2]
  • len(path)=2

  • 还需要 k - len(path) = 1

  • 上界 = 5 - 1 + 1 = 5

👉 for i in range(start, 5)
可以取 i=2,3,4(对应数字 3,4,5)
这时基本没剪枝,因为只需要再补 1 个,随便选。


④ 当 path=[1,2,3]
  • len(path)=3

  • 已经够了,不会再进入循环。


总结成规律:

  • path 短(差很多个数) → 上界更小,循环范围收紧,剪枝多。

  • path 长(差很少个数) → 上界更接近 n,循环范围放宽,剪枝少。

它的作用就是:
保证“至少还能凑齐 k 个数”,避免白白进入不可能成功的递归分支。


要不要我帮你写个小程序,把 len(path) 从 0 到 k-1 的变化,以及对应的上界值打印出来,让你一步步看?


网站公告

今日签到

点亮在社区的每一天
去签到