You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中如何在DFS递归函数内部统计执行次数(无需全局变量)

嘿,这两个递归统计的问题我很熟悉,尤其是把全局变量整合到函数内部的需求,刚好可以用几种优雅的方式解决,我一步步给你讲~

一、通用:统计递归函数执行次数的常见方法

你之前用全局变量ind的方式虽然能实现,但容易引发副作用(比如多次调用会累加计数),而且不够封装。下面是几种把计数器整合到函数内部的方案:

1. 闭包(嵌套函数)—— 最推荐的方式

把计数器放在外层函数里,内层递归函数通过闭包访问它,完全不需要全局变量,封装性拉满。

针对你的BST DFS场景,代码示例如下:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

# 构建包含[1,2,...,15]的二叉搜索树(这里是完全二叉树结构)
def build_bst():
    nodes = [TreeNode(i) for i in range(1, 16)]
    for i in range(7):
        nodes[i].left = nodes[2*i+1]
        nodes[i].right = nodes[2*i+2]
    return nodes[0]  # 根节点为8

def count_dfs_executions(root):
    count = [0]  # 用列表是因为它是可变对象,闭包内能直接修改元素值
    
    def dfs(node):
        count[0] += 1  # 每次进入DFS函数就计数+1(包括空节点的调用)
        if not node:
            return
        dfs(node.left)
        dfs(node.right)
    
    dfs(root)
    return count[0]

# 测试
root = build_bst()
print(count_dfs_executions(root))  # 输出31,和你说的一致

解释:外层函数初始化计数器,内层dfs函数每次被调用(不管节点是否为空)都会让计数器加1,最后返回统计结果。用列表而不是普通整数,是因为整数是不可变对象,闭包内无法直接修改外层的整数变量,而列表是可变的,修改元素没有问题。

2. 可变默认参数—— 简洁但需注意副作用

这种方式更简单,直接把计数器作为函数的默认参数(必须用可变对象,比如列表):

def dfs(node, count=[0]):
    count[0] += 1
    if not node:
        return
    dfs(node.left)
    dfs(node.right)
    return count[0]

# 测试
root = build_bst()
print(dfs(root))  # 第一次调用输出31

# 注意:如果要多次调用,必须手动重置计数器,因为默认参数只在函数定义时初始化一次
dfs(None, count=[0])  # 重置计数

这种方式的缺点是如果多次调用不重置计数器,会导致结果累加,所以适合单次调用场景,或者每次调用前手动重置。

3. 类封装—— 适合复杂场景扩展

如果你的递归逻辑比较复杂,或者需要扩展其他功能,用类封装计数器是个不错的选择:

class DFSCounter:
    def __init__(self):
        self.count = 0
    
    def dfs(self, node):
        self.count += 1
        if not node:
            return
        self.dfs(node.left)
        self.dfs(node.right)

# 使用
counter = DFSCounter()
root = build_bst()
counter.dfs(root)
print(counter.count)  # 输出31

类的方式把计数器作为实例属性,每次创建新实例就会自动重置计数,不会有多次调用的副作用,而且扩展性强,后续可以轻松添加其他统计功能。

二、针对你的BST DFS场景的验证

你提到的包含1-15的二叉搜索树,DFS执行次数为31次是完全正确的:15个非空节点各被调用1次,加上16个空指针(比如叶子节点的左右子树)的调用,15+16=31次,上面的代码刚好统计了所有函数调用次数,所以输出符合预期。

总结

相比全局变量,上面的几种方法都实现了计数器的内部化,避免了全局变量的副作用。其中**闭包(嵌套函数)**是最简洁且安全的方式,推荐优先使用;如果需要更复杂的逻辑,类封装会更合适。

内容的提问来源于stack exchange,提问作者pinseng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:34:23