🌳 想象你的树是一座魔法森林
假设你有一座魔法森林,里面的每棵树都有一些奇怪的连接:
- 🌲 左孩子:指向自己的左边
- 🌳 右孩子:指向自己的右边
- 🎲 随机指针:随意指向森林中的某棵树(甚至可能是自己)
现在,你需要施展魔法,把这整片森林完整地复制一份,包括这些奇怪的连接方式!
✨ 解题思路(魔法步骤)
-
创建一本魔法书📖(HashMap)
- 这本书会记录:原始树的节点 -> 新拷贝出来的节点 的映射关系。
- 这样我们在遇到一个节点时,就知道是否已经复制过了。
-
使用DFS进行复制🧙♂️
-
每次遇到一个新节点:
- 复制它,并存入魔法书
- 递归地处理它的左、右、随机指针
- 更新新节点的指针指向
-
-
返回新复制的根节点🌱
📌 代码java
public TreeNode copyTree(TreeNode root) {
Map<TreeNode, TreeNode> copiedMap = new HashMap<>();
dfs(root, copiedMap);
return copiedMap.get(root);
}
private void dfs(TreeNode root, Map<TreeNode, TreeNode> copiedMap) {
if (copiedMap.containsKey(root)) return; // 如果已经复制过,直接返回
// 创建新节点
TreeNode copiedRoot = new TreeNode(root.val);
copiedMap.put(root, copiedRoot); // 记录映射关系
// 递归复制左孩子
if (root.left != null) {
dfs(root.left, copiedMap);
copiedRoot.left = copiedMap.get(root.left);
}
// 递归复制右孩子
if (root.right != null) {
dfs(root.right, copiedMap);
copiedRoot.right = copiedMap.get(root.right);
}
// 递归复制随机指针
if (root.random != null) {
dfs(root.random, copiedMap);
copiedRoot.random = copiedMap.get(root.random);
}
}
🎯 代码解读(通俗版)
-
我先翻开魔法书📖,看看这个节点有没有被记录过
- 如果已经复制过,那就直接返回,不重复造树。
-
如果这个节点没见过,那我就施展魔法🪄,复制它
- 创建一个新节点(跟原来的值一样)。
- 存进魔法书,方便后续查找。
-
然后去复制它的“左边”“右边”和“随机指针”
- 先去左边看看👈,如果有左孩子,就递归地复制。
- 再去右边瞧瞧👉,如果有右孩子,也递归地复制。
- 最后看看这个节点的“随机指针”🎲指向哪里,复制后也连上。
-
所有节点都处理完后,返回新树的根节点🌱!
💡 为什么要用 HashMap?
你可能会问:“为什么要用 HashMap 记录已经复制过的节点?”
原因是:
- 防止重复复制:如果某个节点已经被复制过,就直接复用它,避免陷入无限递归。
- 处理“随机指针” :树的
random指针可能指向任意一个已经遍历过的节点,如果不记录它的位置,后续查找就会很麻烦。
🏆 总结
✅ 这道题的核心思想是用哈希表(Map)来记录原节点和新节点的映射关系,然后递归地复制整棵树。
✅ 每个节点最多访问三次(左、右、随机),时间复杂度是 O(N),空间复杂度也是 O(N)(存储 Map) 。
✅ 这样,我们就成功复制了一片一模一样的魔法森林🌲🌳🎲!