树的结构是面试中最常考察的一种数据结构,因为这不至于太难,又能过滤掉一批基础薄弱的面试者,所以对树的理解深度很大程度影响是否能顺利通过面试。 这次来分析一个树的经典算法题目:
题目
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。
示例 1:
输入: [1,2,3]
1
/
2 3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.
示例 2:
输入: [4,9,0,5,1]
4
/
9 0
/
5 1
输出: 1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495.
从根到叶子节点路径 4->9->1 代表数字 491.
从根到叶子节点路径 4->0 代表数字 40.
因此,数字总和 = 495 + 491 + 40 = 1026.
分析思路
树的问题,我们会很容易想到递归去解决,这个要把每个路径的数值进行相加,那我们就要找到每一个分支的方法,其实在之前遇到的某些树的问题上面都需要对叶子节点反向寻找父亲节点的时候会采用一种Map的方式维护子节点与父节点的指针关系,这样其实就相当于变相增加了节点的指针数,从只有左右两个指针增加一个父节点的指针,根据这个思路我们可以通过DFS构建这个指针,并且可以通过所有叶子节点去迭代查询到根节点,并累加得到结果,并把结果进行累加。
具体代码实现:
Map<TreeNode, TreeNode> parent = new HashMap<>();
List<TreeNode> leafList = new ArrayList<>();
int sum = 0;
public int sumNumbers(TreeNode root) {
if (root == null) return 0;
dfs(root, null);
int ret = 0;
for (TreeNode node : leafList) {
int n = 1;
int sum = n * node.val;
while (parent.get(node) != null) {
TreeNode p = parent.get(node);
n *= 10;
sum += (n * p.val);
node = p;
}
ret += sum;
}
return ret;
}
public void dfs(TreeNode treeNode, TreeNode parentNode) {
if (treeNode == null) return ;
parent.put(treeNode, parentNode);
dfs(treeNode.left, treeNode);
dfs(treeNode.right, treeNode);
if (treeNode.left == null && treeNode.right == null) leafList.add(treeNode);
}
复杂度分析
这个代码用了空间复杂度n(n为节点数量) + m(叶子节点数量),当然leafList可以省略掉,直接遍历parent所有节点,如果左右节点都为空即为叶子节点。
时间复杂度为2n即为n。
优化思路
其实上面的方法并不够好,还有优化空间,在真实面试的场景下上面的算法大概率会被面试官否定,会要求用常数级的空间复杂度去实现,那又该如何去解决呢?
认真分析发现,每个路径从根向叶子节点递归,如果有子节点的值*10,继续递归到子节点。当遇到叶子节点,即为当前路径的和,把它加到一个结果里即可。
关键就是找到递归入参数和递归出口,根据上面的分析我们得到:
入参,当前路径已累计值,当前节点。
递归出口,当前节点为叶子节点,即为出口,累加结果。
代码实现:
int ret = 0;
public int sumNumbers(TreeNode root) {
sumNumbers(root, 0);
return ret;
}
public void sumNumbers(TreeNode root, int sum) {
if (root == null) return ;
sum += root.val;
if (root.left == null && root.right == null) ret += sum;
sumNumbers(root.left, sum * 10);
sumNumbers(root.right, sum * 10);
}
其中较为巧妙的是每往子节点递归的时候,当前值都要*10,因为有子节点意味着当前的值是10进制的高位需要提位。
复杂度分析
空间复杂度 常数, 时间复杂度 n,大大进行了优化,相信面试官一定能够满意。