移动 N 叉树的子树
难度:
标签:
题目描述
代码结果
运行时间: 61 ms, 内存: 17.0 MB
// 题目思路:
// 给定一个 N 叉树,和两个节点 `node` 和 `newParent`。
// 要求把 `node` 及其子树移动到 `newParent` 的子节点中。
// 假设输入的树节点具有以下结构:
// class Node {
// public int val;
// public List<Node> children;
// public Node() {}
// public Node(int _val) {
// val = _val;
// }
// public Node(int _val, List<Node> _children) {
// val = _val;
// children = _children;
// }
// }
import java.util.*;
import java.util.stream.*;
class Solution {
public Node moveSubTree(Node root, Node node, Node newParent) {
if (root == null || node == null || newParent == null) return root;
// 如果 newParent 是 node 或 node 的子节点,直接返回 root
if (isDescendant(node, newParent)) return root;
// 移除 node
removeNode(root, node);
// 将 node 添加到 newParent 的子节点
newParent.children = Stream.concat(newParent.children.stream(), Stream.of(node))
.collect(Collectors.toList());
return root;
}
private boolean isDescendant(Node node, Node target) {
return node == target || node.children.stream().anyMatch(child -> isDescendant(child, target));
}
private void removeNode(Node parent, Node node) {
parent.children = parent.children.stream()
.filter(child -> !child.equals(node))
.collect(Collectors.toList());
parent.children.forEach(child -> removeNode(child, node));
}
}
解释
方法:
此题解的核心思路是通过一次深度优先搜索(DFS)找到节点p和q的父节点以及判断p是否位于q的子树中。首先,创建一个虚拟根节点,其唯一的子节点是真实的根节点,这样可以统一处理边界条件。在DFS过程中,如果遇到节点p,则记录其父节点并标记当前路径下所有节点都在p的子树中。类似地,遇到节点q时记录其父节点。若此时已标记在p的子树中,则表明q在p的子树中。处理移动逻辑时,考虑两种情况:若p直接是q的父节点,则不需要移动;若p在q的子树中,需要先从q的父节点子列表中移除q,再将q作为p的子节点。若p不在q的子树中,则从p的父节点子列表中移除p,并将p作为q的子节点。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
在DFS过程中,如何确保只访问每个节点一次,避免重复访问造成的性能问题?
▷🦆
创建虚拟根节点的策略主要是为了解决哪些边界条件?具体是如何简化问题的?
▷🦆
在执行`q_fa.children.remove(q)`和`p_fa.children.remove(p)`时,如果子节点列表很大,这些操作的效率如何?是否有更高效的数据结构来处理这种删除操作?
▷🦆
代码中如何处理p和q是同一个节点的情况?这种情况下的移动逻辑是什么?
▷