题目来源:https://leetcode-cn.com/problems/copy-list-with-random-pointer/
大致题意:
给定一个链表,其中每个节点有两个指针,一个为 next 指向下一个节点,一个为 random 指向随机一个节点(可能为空)。要做的就是复制给定链表。
需要注意,返回结果时,不能返回原链表,也要保持原链表的完整性。
思路
不能直接遍历复制,因为有random指针,其指向的节点可能还未遍历到,也就是还未复制,那么就无法指向该节点。
那么就需要想办法,让新复制的节点的random指针指向正确
哈希表 + 递归
使用一个哈希表存已经创建的节点。键 为 原表节点,值 为 新创建的节点。
- 每次递归时,先判断当前节点是否为空,若为空直接返回空;若不为空,则判断当前节点是否已经创建,使用HashMap.containsKey方法即可。
- 若当前节点未创建,则创建,并放入哈希表。再调用递归函数遍历next和random。结束后返回当前节点
- 若已经创建,直接返回当前节点。
代码:
Map<org.w3c.dom.Node, org.w3c.dom.Node> existed = new HashMap<Node, Node>();
public Node copyRandomList(Node head) {
if (head == null) { // 判断是否为空
return null;
}
if (!existed.containsKey(head)) { // 判断是否已经复制
Node node = new Node(head.val); // 创建
existed.put(head, node); // 放入Map
// 递归
node.next = copyRandomList(head.next);
node.random = copyRandomList(head.random);
}
return existed.get(head);
}
复制 + 剔除
最终返回结果时,要保证原链表是没有改变的。
那么我们可以在解题过程中对其改变!
- 第一次遍历,复制每个当前节点,然后新复制的节点的 next 指向 当前节点的 next 指向的节点,再让 当前节点的 next 指向 新复制的节点。
- 第二次遍历,只遍历原链表的节点,所以循环变量 node 每次都是为next的next。即 node = node.next.next。同时每次让循环变量的next,也就是当前节点的复制节点的 random指针 指向当前节点的random指针指向节点的next(也就是当前节点的random所指节点的复制节点)。
- 第三次遍历,用作剔除。将复制的节点取出,需要修改next指针。random指针在上一轮已经正确指向。
代码:
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
// 第一次遍历,复制节点
for (Node node = head; node != null; node = node.next.next) {
Node interNew = new Node(node.val);
// 更改指向,新旧节点串一起
interNew.next = node.next;
node.next = interNew;
}
// 第二次遍历,更新random指针
for (Node node = head; node != null; node = node.next.next) {
Node interNode = node.next;
// 修改复制节点的random指针
// 加上特判,若为空代表random指向空,无next
interNode.random = (node.random != null) ? node.random.next : null;
}
// 创建新链表头结点
Node newHead = head.next;
// 第三次遍历,剔除新节点,加入新链表
for (Node node = head; node != null; node = node.next) {
Node interNode = node.next;
// 更改旧节点指向旧节点
node.next = node.next.next;
// 更改新节点指向新节点
// 加上特判,若为尾结点则next为空
interNode.next = (interNode.next == null) ? null : interNode.next.next;
}
return newHead;
}