题目描述
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 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 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
方法一:
1、首先考虑边界,当head为空时,不用拷贝,直接返回head。
2、如果不考虑random指针,复制一个链表很简单,可以新建一个newHead引用,然后依次遍历原链表,然后对于每一个节点,用其值新建节点,并依次将这些节点链接在新的链表newHead上。
3、在本题中,对于每个节点还存在一个随机指向另一个节点的指向信息需要拷贝。在前面所述的拷贝操作中,可以将新链表的节点引用依次存放在一个数组中,就可通过访问数组下标来快速的获得新链表中各个节点的引用,对于新链表的每个节点,如果我们知道了旧链表中random指向节点的位置index,那么让每个节点根据位置信息,指向刚刚所建立的数组中的元素,即可完成深拷贝。
4、为了获取旧链表中指定节点的位置,可以从头遍历链表并维护一个位置index,遍历一个节点,index++,当前节点为所要寻找节点,返回index。
针对每一个节点在获取random的位置信息时,最坏的时间复杂度是O(n),即遍历整个链表,时间复杂度O(n2)
方法一实现代码(Java):
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public int findIndex(Node head,Node fn)
{
Node temp=head;
int index=0;
while(temp!=null&&fn!=temp)
{
index++;
temp=temp.next;
}
return index;
}
public Node copyRandomList(Node head) {
if(head==null)
{
return head;
}
Node newHead=new Node(head.val);
Node p1=head.next;
Node p2=newHead;
List<Node> nodes=new ArrayList<Node>();
nodes.add(newHead);
while(p1!=null)//不考虑随机节点下复制
{
Node node=new Node(p1.val);
p2.next=node;
nodes.add(node);
p2=p2.next;
p1=p1.next;
}
p2=newHead;
p1=head;
while(p2!=null)
{
Node node=p1.random;
if(node!=null)
{
int index=findIndex(head,node);
p2.random=nodes.get(index);
}
p1=p1.next;
p2=p2.next;
}
return newHead;
}
}
方法二:由上图可以发现,这种方法效率很低。方法一存在一个问题,针对每一个节点,在复制节点的random信息时,为了获取特定节点的位置信息时,需要去从头遍历节点,其实在遍历过程,针对该特定节点之前节点的位置信息,我们已经获得,但是下次需要这些信息时,我们需要再次重复计算,所以在整个计算过程造成了大量的资源浪费。如果将这些位置信息保存下来,就会大大的提高程序的运行速度。
故方法二中,我采用hashmap<Node,Integer>,在第一次不考虑随机节点遍历时,就记录下原始链表中各个节点的位置信息:
HashMap<Node,Integer> map=new HashMap<>();
map.put(node,index)
然后利用map的get方法就可以快速的获取原始链表指定节点的位置信息,且不会造成重复运算。
方法二实现代码(Java)
class Solution {
public Node copyRandomList(Node head) {
if(head==null)
{
return head;
}
Node newHead=new Node(head.val);
Node p1=head.next;
Node p2=newHead;
List<Node> nodes=new ArrayList<Node>();
HashMap<Node,Integer> map=new HashMap<>();
nodes.add(newHead);
int index=0;
map.put(head,index);
while(p1!=null)
{
index++;
map.put(p1,index);//记录原始链表的位置信息
Node node=new Node(p1.val);
p2.next=node;
nodes.add(node);
p2=p2.next;
p1=p1.next;
}
p2=newHead;
p1=head;
while(p2!=null)
{
Node node=p1.random;
if(node!=null)
{
p2.random=nodes.get(map.get(node));
}
p1=p1.next;
p2=p2.next;
}
return newHead;
}
}