题目
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
提示:
-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。
暴力解法
本题要求的是链表的复制,限制了复制后的结果不能指向原链表,即要求完成的是值的复制,且要求保证原链表不发生变化。
故先将复杂链表复制为数组tmp(此处还是地址的引用),后新建copy数组依次复制tmp中的值,从而实现复杂链表的复制。
class Solution {
public Node copyRandomList(Node head) {
if( head == null )
return null;
//因为要求不改变原链表,所以先复制一遍
ArrayList<Node> tmp = new ArrayList<>();
while( head != null ) {
tmp.add(head);
head = head.next;
}
//不能直接return tmp.get(0),因为这样引用的地址还是一样的
//这里每个值都不能直接引用,只能取它的具体值!!!
//正式开始复制链表
ArrayList<Node> copy = new ArrayList<>();
for( int i = 0; i<tmp.size(); ++i )
copy.add( new Node(tmp.get(i).val) );
//复制val值
for (int i = 0; i < tmp.size(); ++i) {
//复制random值
copy.get(i).random
= tmp.get(i).random == null?null:copy.get(tmp.indexOf(tmp.get(i).random));
/*random指向copy.get(x),且copy.get(x) = tmp.get(x)
其中 x = indexOf(tmp.get(i).random),*/
if( i != tmp.size()-1 )
copy.get(i).next = copy.get(i+1);
//复制next值
}
return copy.get(0);
}
}
官方迭代法
思路:将A’插入A和B之间,并给其赋val,random值,最后将新旧链表分开。
class Solution {
public Node copyRandomList(Node head) {
if (head == null)
return null;
/*指向新节点,又回到旧节点,即A → A'→ B → B'→ C
实际作用大概是复制val*/
for (Node node = head; node != null; node = node.next.next) {
Node nodeNew = new Node(node.val);
nodeNew.next = node.next;//先连哪个指针是有讲究的捏
node.next = nodeNew;
}
//复制random
for (Node node = head; node != null; node = node.next.next) {
Node nodeNew = node.next;//nodeNew即新节点,A',B'等
nodeNew.random = (node.random != null) ? node.random.next : null;
//random.next是因为要指的是新节点啦
}
//将新旧链表分开
Node copy = head.next;
for (Node node = head; node != null; node = node.next) {
Node nodeNew = node.next;
node.next = node.next.next;
nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
}
return copy;
}
}
哈希表实现
实现思路:运用map实现由原节点到新节点的映射。遍历链表过程中,需要检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,递归进行创建。
class Solution {
Map<Node, Node> cachedNode = new HashMap<Node, Node>();
public Node copyRandomList(Node head) {
if (head == null)
return null;
if ( !cachedNode.containsKey(head) ) {//若头节点尚未建立映射关系
Node headNew = new Node(head.val);
cachedNode.put( head, headNew );
headNew.next = copyRandomList(head.next);
headNew.random = copyRandomList(head.random);
}
return cachedNode.get(head);
}
}