二叉搜索树递归求高度:两种实现方案的差异对比与代码精简优化咨询
嘿,很高兴你在琢磨代码优化的事儿!咱们来一步步拆解这两个二叉搜索树高度计算实现的差异,以及你的代码里可以改进的地方:
两种递归实现的核心差异
- 状态管理逻辑完全不同
你的代码用了全局静态变量countL和countR来记录左右子树的“计数”,而网上的实现是用局部变量在递归栈里传递状态。全局变量相当于把状态暴露在函数外部,会带来很多隐患;而局部变量的状态只在当前递归层级生效,逻辑更闭环。 - 递归的本质理解有偏差
你的实现是通过遍历节点时累加计数来“凑”高度,但正确的递归逻辑应该是当前节点高度 = 子树高度 + 1——网上的实现正是遵循这个逻辑,每次递归调用子树拿到高度后,加1得到当前节点的高度,再比较左右取最大值。 - 代码的可靠性与复用性差异
网上的实现是纯函数(输入相同则输出相同,没有外部副作用),多次调用也不会出问题;而你的代码依赖全局变量,第二次调用时countL和countR不会自动重置,直接导致结果错误,而且多线程环境下还会出现线程安全问题。
你的代码存在的具体问题
- 全局变量的副作用:多次调用
getHeight时,静态变量的残留值会彻底打乱计算结果,比如第一次计算完左斜树后countL=5,第二次计算右斜树时,初始countL还是5,结果完全错误。 - 递归逻辑错误:你只是单纯计数遍历过的节点数,没有处理递归回溯的情况。比如遍历完左子树回到父节点,再遍历右子树时,
countL并没有被重置,最终Math.max(countL, countR)得到的不是左右子树的高度最大值,而是整个树里左节点总数和右节点总数的最大值,这和“高度”的定义完全不符。 - 边界情况未处理:如果传入
root为null(空树),你的代码会直接抛出空指针异常,而健壮的实现应该先处理这种边界情况。
代码精简与编码水平提升建议
- 彻底抛弃全局变量存储临时状态
递归函数的状态应该通过返回值或参数传递,像网上的实现那样用局部变量接收子树的返回值,再计算当前节点的高度。你甚至可以把代码精简到极致,更清晰地体现递归逻辑:private static int getHeight(Node root) { // 空树高度定义为-1(边数),如果是层数则返回0 if (root == null) return -1; return Math.max(getHeight(root.left), getHeight(root.right)) + 1; } - 吃透递归的“分治+回溯”本质
处理树的问题时,递归的核心是把大问题拆成子问题:计算当前树的高度,只需要知道左右子树的高度,取最大值加1即可。不需要手动计数,让递归栈帮你处理子问题的结果传递。 - 注重代码的“无副作用”与可读性
写代码时要考虑:别人能不能一眼看懂逻辑?多次调用会不会出问题?纯函数(不修改外部状态)更容易维护、测试和复用。 - 多测试边界情况
比如空树、只有根节点的树、左斜树、右斜树,这些场景能快速暴露代码的问题。比如你的代码在空树场景直接报错,而优化后的版本会正确返回-1(或0,根据高度定义调整)。 - 参考经典算法的标准实现
树的高度计算是经典问题,多看看这类问题的标准实现,理解背后的逻辑,而不是自己用全局变量去“凑”结果,慢慢就能养成更专业的编码习惯。
内容的提问来源于stack exchange,提问作者j bel
相关产品推荐
相关产品推荐

