请解释这段求解目标值组合的递归代码工作原理
拆解这段递归组合求和代码的逻辑
首先先提个小问题:你贴的代码里函数参数是distances,但函数内部用了d,这是笔误,得改成一致的(比如都用distances),不然运行会报NameError。接下来咱们一步步拆解它的递归逻辑:
核心功能
这段代码的目的是找出distances列表中所有**元素相加等于目标值r**的组合——注意因为会遍历整个列表的元素,所以列表里的重复元素(比如示例里的两个5)会生成对应的有效组合。
递归的终止条件
递归函数的关键就是先明确终止情况:
- 当
r < 0:说明当前选择的元素加起来已经超过目标值了,这条路走不通,直接返回空列表[],表示没有可行解。 - 当
r == 0:说明刚好凑到了目标值,这时候返回[[]]——一个包含空列表的列表。这个空列表很重要,它是后续追加元素的“容器”,相当于告诉上层递归:“我找到了一个凑到0的方法,你把刚才选的元素加进来就行”。
递归的核心循环逻辑
接下来是最关键的循环部分,咱们逐行拆解:
- 初始化空列表
solutions,用来存储所有有效的组合。 - 遍历
distances里的每一个元素last_distance:- 递归调用
greedy(r - last_distance, distances):意思是假设我现在选了这个last_distance元素,那剩下需要凑的目标值就是r - last_distance,我去递归找能凑出这个剩余值的所有组合。 - 遍历递归返回的每一个组合
combo:把当前的last_distance追加到combo末尾。比如递归返回[[5]](表示能凑出5的组合是[5]),追加当前的5就变成[[5,5]],这就是一个和为10的有效组合。 - 检查这个组合是不是已经在
solutions里了,如果没有就加进去,避免重复的组合被存入。
- 递归调用
用你的示例走一遍流程
拿你给的例子:distances = [3,5,2,5],目标r=10
- 第一次循环选
last_distance=3,递归找r=7的组合:- 在
r=7的递归里,选last_distance=5,递归找r=2的组合:- 在
r=2的递归里,选last_distance=2,递归找r=0,返回[[]],把2追加进去得到[[2]]。 - 回到
r=7的层,把5追加到[2]里,得到[[2,5]]。 - 回到最外层,把3追加到
[2,5]里,得到[[2,5,3]](也就是你说的[3,2,5],只是顺序反过来了,组合是有效的),加入solutions。
- 在
- 在
- 然后循环选
last_distance=5,递归找r=5的组合:- 在
r=5的递归里,选last_distance=5,递归找r=0返回[[]],追加5得到[[5]]。 - 回到最外层,把5追加到
[5]里,得到[[5,5]],加入solutions。
- 在
- 后面遍历2和第二个5的时候,可能会生成类似
[5,3,2]或者重复的[5,5],但因为solutions.__contains__(combo)的检查,重复的组合不会被再次加入。
代码可以优化的点
- 参数名不一致:刚才说的,函数参数
distances和内部的d要统一,不然会报错。 - 去重效率低:
solutions.__contains__(combo)是遍历整个列表检查,数据量大的时候很慢。而且如果把[3,2,5]和[2,3,5]当成同一个组合的话,应该先对组合排序再检查,不然这两个会被当成不同的组合存入。 - 重复计算:递归过程中会多次计算同一个
r的情况,比如多次计算r=5,可以用记忆化(比如用字典存已经计算过的r对应的结果)来大幅提升效率。
内容的提问来源于stack exchange,提问作者ali light
相关产品推荐
相关产品推荐

