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

LeetCode 113. Path Sum II时间复杂度分析及优化方法问询

LeetCode 113. Path Sum II 问题解答

问题描述

我完成了LeetCode挑战113. Path Sum II:题目要求给定二叉树根节点和目标和targetSum,返回所有根到叶子节点的路径,路径节点值之和等于targetSum,路径以节点值列表形式返回。

我的JavaScript实现代码

var pathSum = function(root, targetSum) {
    let paths = [];
    if(root === null) {
        return [];
    }
    getAllSumPaths(root, [], paths, targetSum);
    return paths;
}; 

function getAllSumPaths(root, currPath, paths, targetSum) {
    if(root.left === null && root.right === null) {
        if(targetSum - root.val === 0) {
            currPath.push(root.val);
            paths.push([...currPath]);
            currPath.pop();
        }
        return;
    }
    currPath.push(root.val);
    if(root.left !== null){
        getAllSumPaths(root.left, currPath, paths, targetSum - root.val);
    }
    if(root.right !== null){
        getAllSumPaths(root.right, currPath, paths, targetSum - root.val);        
    }
    currPath.pop();
} 

疑问

我原本认为算法时间复杂度是O(n)(n为二叉树节点数),但因需要用扩展运算符复制有效路径到paths数组(避免后续pop操作修改已存入的路径),而该操作的时间复杂度为O(k)(k为路径长度),不确定如何计算整体时间复杂度,同时想了解是否有其他写法可避免该O(k)操作。


解答

时间复杂度分析

你的整体时间复杂度其实是O(n + L×k),拆解来看:

  • n是二叉树的总节点数,每个节点都会被遍历一次,这部分开销是O(n);
  • L是满足条件的根到叶子路径的数量,k是每条路径的平均长度。

极端场景下,比如满二叉树且所有路径都符合目标和,此时L等于叶子节点数(约n/2),每条路径长度k是O(logn),整体复杂度会是O(n + nlogn),也就是O(nlogn);如果只有一条符合条件的路径,那复杂度就是O(n + k),接近O(n)。

本质上,复制路径的开销是无法完全规避的——因为我们需要保存符合条件的路径,这些路径本身的总长度就是所有符合条件路径的节点数之和,这部分开销是必然存在的,只是实现方式不同而已。

避免显式回溯复制的写法

可以换一种递归思路,每次递归时创建新的路径数组,不用复用同一个currPath,这样就不需要回溯时的pop操作:

var pathSum = function(root, targetSum) {
    let paths = [];
    if (!root) return paths;
    
    function dfs(node, currentSum, currentPath) {
        // 到达叶子节点
        if (!node.left && !node.right) {
            if (currentSum + node.val === targetSum) {
                paths.push([...currentPath, node.val]);
            }
            return;
        }
        
        // 递归左子树,传递新的路径数组
        if (node.left) {
            dfs(node.left, currentSum + node.val, [...currentPath, node.val]);
        }
        // 递归右子树,传递新的路径数组
        if (node.right) {
            dfs(node.right, currentSum + node.val, [...currentPath, node.val]);
        }
    }
    
    dfs(root, 0, []);
    return paths;
};

这种写法虽然不用手动pop,但本质上还是会在递归传递时产生O(k)的复制开销,整体时间复杂度和你的原写法完全一致——不管是在叶子节点复制,还是在递归时复制,最终所有符合条件路径的总长度开销是相同的。

还有一种思路是把路径转成字符串暂存,最后再转回数组,但这种写法可读性差,而且字符串转数组的开销也不会比直接复制数组小,不推荐使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 12:45:37