leetcode
leetcode 1051 ~ 1100
删除树节点

删除树节点

难度:

标签:

题目描述

代码结果

运行时间: 52 ms, 内存: 18.0 MB


/*
 * Problem 1273: Delete Tree Nodes
 * Given a tree represented by an integer array nodes, where nodes[i] is the value of the ith node. 
 * The tree has n nodes with node 0 as the root. The array parent of length n, where parent[i] is the parent of the ith node.
 * Delete the subtrees of nodes with a value of 0 and return the number of remaining nodes.
 * 
 * Approach using Java Streams:
 * 1. Parse the parent array to construct the tree structure.
 * 2. Use a depth-first search (DFS) to traverse the tree using streams.
 * 3. Calculate the sum of subtree values and count nodes.
 * 4. If the sum of a subtree is zero, exclude it from the count.
 */

import java.util.*;
import java.util.stream.*;

public class Solution {
    public int deleteTreeNodes(int nodes, int[] parent, int[] value) {
        Map<Integer, List<Integer>> tree = IntStream.range(0, nodes).boxed()
            .collect(Collectors.groupingBy(i -> parent[i]));

        return dfs(0, tree, value)[1];
    }

    private int[] dfs(int node, Map<Integer, List<Integer>> tree, int[] value) {
        int sum = value[node];
        int count = 1;
        if (tree.containsKey(node)) {
            for (int child : tree.get(node)) {
                int[] result = dfs(child, tree, value);
                sum += result[0];
                count += result[1];
            }
        }
        if (sum == 0) {
            count = 0;
        }
        return new int[]{sum, count};
    }
}

解释

方法:

本题解采用拓扑排序的方法来解决删除树节点的问题。首先,计算每个节点的入度,然后使用一个队列来存储所有入度为0的节点(即叶子节点)。从这些叶子节点开始,逐步向上更新其父节点的节点值和节点数量。如果某个节点的值更新后为0,则该节点以及其所有子节点都不应被计算在内,因此将其节点数量设置为0。最终,队列中会处理每个节点,直到到达根节点。这种方法从底部叶子节点向上至根节点逐层剪枝,直到所有的节点都被处理过。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在解决删除树节点这个问题时,为什么选择使用拓扑排序而不是其他如深度优先搜索(DFS)或广度优先搜索(BFS)的方法?
拓扑排序是处理具有方向性从属关系的问题的一种有效方法,特别是在处理需要从子节点向父节点聚合信息的场景中。在此题中,从叶子节点开始向根节点聚合节点的值和计数信息,能够确保在处理每个节点时,其所有子节点的信息已经被完全处理和更新,这样可以有效地减少重复计算和回溯,提高效率。相比之下,DFS和BFS虽然也能实现节点的遍历,但在处理节点删除或值更新时,可能需要更复杂的回溯操作或多次遍历。
🦆
如何处理树中某个节点的值更新为0之后,其对应的子节点是否也应该被考虑为删除或值为0?
在题解中,当某个节点的值更新为0后,该节点以及其所有子节点应被视为在最终的节点计数中不再被包含。这是因为题目的目的是计算保留的有效节点的总数,节点值为0意味着这个节点及其子树不应被计入最终的统计结果中。因此,在实现中,节点值为0时会将该节点的节点计数设置为0,并且在更新父节点时,不再将这些节点的计数加到父节点上。
🦆
在更新父节点的值和节点数量时,如果父节点的值变为0,是否还需要继续更新其父节点的值和节点数量?
在题解的逻辑中,即使某个父节点的值在更新后变为0,其父节点的值和节点数量仍需要继续更新。这是因为父节点的值变为0是在加入子节点的值之后的结果,而其它子节点的值可能仍然需要加到这个父节点上。只有当处理到这个父节点本身,并发现其值为0时,才将其计数设置为0,并在后续的更新中不再计入其父节点的计数。
🦆
代码中使用了队列来存储入度为0的节点,这种做法在实际执行中是如何保证从叶子节点逐层向上更新的?
在代码实现中,首先识别所有入度为0的节点,即叶子节点,将它们加入队列。这些节点是没有子节点的,因此可以安全地开始处理它们。在处理过程中,每当一个节点被处理(即从队列中移除并更新其父节点的值和计数),其对应的父节点的入度会减少。当父节点的入度减少到0时,意味着该父节点的所有子节点都已处理完毕,此时将父节点加入队列。这个逐层处理保证了从叶子节点向上至根节点的正确更新顺序。

相关问题