PHP递归遍历前必须先判空,否则访问null对象属性会触发Fatal error;正确做法是入口处立即if(!$node) return;BST场景还需按值大小剪枝子树,避免退化为O(n)。

PHP递归遍历前必须判空,否则触发Fatal error: Uncaught Error: Call to a member function xxx() on null
PHP里访问 $node->left 或 $node->right 前不检查 $node 是否为 null,运行时直接报错。这不是警告,是致命错误,整个脚本中断。核心原因:PHP对象属性访问语法不自动短路,null->left 无法被容忍。
正确做法是在每次递归入口第一行加 if (!$node) return;(或对应逻辑,如 return [];)。这个判断不能省,也不能挪到中间——因为一旦进入函数体,后续任意一次 $node->left 都可能炸。
- 常见错误写法:
if ($node->left) { preorderTraversal($node->left); }—— 这里先用了$node->left才判断,已经晚了 - 正确顺序:先
if (!$node) return;,再放心用$node->left和$node->right - 注意
== null、=== null、!$node在null/false/0上行为不同;推荐用!$node,它能同时覆盖null和未初始化状态,且最简洁
递归查找目标节点时,别用 or 短路拼接返回值
像 return $this->find($node->left, $val) or $this->find($node->right, $val); 这种写法看似简洁,但实际会丢节点:PHP 的 or 是布尔运算符,返回的是 true 或 false,不是原值。真正该用的是 ?:(空合并)或显式分支判断。
更危险的是,如果左子树返回的是 0、false 或空字符串,or 会误判为“空”,跳过本应有效的结果。
立即学习“PHP免费学习笔记(深入)”;
- 安全写法一(推荐):
if ($left = $this->find($node->left, $val)) return $left; - 安全写法二:
return $this->find($node->left, $val) ?: $this->find($node->right, $val); - 绝对避免:
return $this->find($node->left, $val) or $this->find($node->right, $val);
中序/后序遍历中,递归调用顺序不影响空指针,但影响逻辑正确性
空节点防护只跟“是否提前判空”有关,跟 left 和 right 调用顺序无关。但如果你写的是中序遍历却把 $node->val 放在最前面,那就不是中序了——这不会报错,但结果完全错乱,调试时极难发现。
PHP没有编译期类型检查,所以结构对但语义错的代码能跑通,只是结果不对。建议严格按遍历定义组织代码块顺序:
- 前序:
if (!$node) return; ... 处理 $node->val; $this->preorder($node->left); $this->preorder($node->right); - 中序:
if (!$node) return; $this->inorder($node->left); ... 处理 $node->val; $this->inorder($node->right); - 后序:
if (!$node) return; $this->postorder($node->left); $this->postorder($node->right); ... 处理 $node->val;
BST场景下强行全树递归是性能陷阱
如果明确知道是二叉搜索树(BST),还写完整左右子树遍历,等于放弃 O(log n) 优势,退化成 O(n)。这时候空指针防护虽然仍要写,但逻辑上应该根据 $val 与 $node->val 大小关系,只进一边子树。
例如查找值:if ($val val) return $this->find($node->left, $val); else return $this->find($node->right, $val);。漏掉这个剪枝,不仅慢,而且在深树上更容易因递归层数过多触发 max_execution_time 超时。
真正容易被忽略的点是:同一个 Node 类,可能被用于普通二叉树,也可能用于 BST——但遍历逻辑必须按使用场景切换,不能复用一套代码硬套两种语义。



















