问题描述:
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
这道题大致的意思就是,一个链表,它每一个节点除了一个next引用以外,还有一个random引用,它指向链表上的任意一个点,或者是null。我们要做的就是深拷贝这个链表——就是重新申请内存建一个一模一样的链表。
对于这道题,我刚开始的想法是先复制一个新的链表,然后通过在原链表中计算当前节点到random指向的节点的距离,然后在新链表的节点上根据距离操作一次,发现这个方法好笨。后来看了别人的博客,头脑才开窍了。
其实你要想在新链表中找到原链表节点的random引用指向的节点对应的节点,那我们可以在复制链表的时候,将原链表的节点与新链表对应的节点相连接,那么当你在访问原链表的节点时,就可以高效地访问到在新链表上对应的节点,random就可以高效的复制。
既然要在原链表和新链表的节点上建立连接,那么其中的一种方法就是使用hashmap,用原链表的节点作键,新链表上对应的节点作值。那么我们在访问原链表上的节点时,就可以直接在新链表上找到对应的节点,再进行赋值,这种方法明显快多了,也方便多了。下面是代码:
public RandomListNode copyRandomList(RandomListNode head) {
if(head == null)
return null;
RandomListNode newHead = new RandomListNode(head.label);
//用hashmap来将旧节点和新节点建立联系
Map<RandomListNode, RandomListNode> tempMap = new HashMap<>();
tempMap.put(head, newHead);
RandomListNode cur = newHead;
//先复制新的链表
for(RandomListNode p = head.next; p != null; p = p.next) {
cur.next = new RandomListNode(p.label);
cur = cur.next;
tempMap.put(p, cur);
}
cur.next = null;
cur = newHead;
//根据旧节点的random,在hashmap中寻找新节点在新链表上对应的ransom,实现高效查找
for(RandomListNode p = head; p != null; p = p.next) {
cur.random = tempMap.get(p.random);
cur = cur.next;
}
return newHead;
}
这种方法很好理解,也很好想, 就是需要o(n)的空间复杂度。还有一种方法可以优化,使得空间复杂度变为常数级。这种方法的核心想法还是将原链表上的节点与新链表上对应的节点建立连接,只不过它是直接将新节点插在对应的旧节点的后面,这个新节点指向原来对应旧节点的下一个旧节点。可以看下面这张图,你们就很容易理解了:
再复制完新链表后,将原链表和新链表合体,变成上图的这种形式,不也就实现了原节点和新节点之间的连接关系吗?然后将新链表上的每个节点的random引用都解决了之后,再将它们分开,就大功告成了。下面是具体代码:
public RandomListNode copyRandomList(RandomListNode head) {
if(head == null)
return null;
RandomListNode newHead = new RandomListNode(head.label);
RandomListNode tempNode = head.next;
head.next = newHead;
newHead.next = tempNode;
RandomListNode oldP = newHead.next;
RandomListNode newP = newHead;
//第一遍遍历,将新链表的每个节点插到原链表的两个节点之间,相当于原链表的每一个节点会连接到新链表对应的节点
while(oldP != null) {
tempNode = oldP.next;
oldP.next = new RandomListNode(oldP.label);
oldP.next.next = tempNode;
newP = oldP.next;
oldP = oldP.next.next;
}
//第二遍遍历,根据上面设置的对应关系,访问原链表节点上的random,然后设置新链表上对应节点的random
oldP = head;
newP = newHead;
while(oldP != null) {
//如果原节点random指向null,新节点random直接指向null
if(oldP.random == null)
newP.random = null;
//否则通过原节点的random找到在新链表中该节点的random需要连接的对应的节点,然后连接
else {
newP.random = oldP.random.next;
}
oldP = oldP.next.next;
if(oldP != null)
newP = newP.next.next;
}
//第三遍遍历,将新链表拆出来
oldP = head;
newP = newHead;
while(newP != null) {
oldP.next = newP.next;
oldP = oldP.next;
if(newP.next != null)
newP.next = newP.next.next;
newP = newP.next;
}
return newHead;
}
这道题总的来说,还是不太难的,重点是,你要想到将原链表上的节点与新链表上的节点连接起来,通过访问原链表上的节点就可以访问新链表上对应的节点,那么这道题就可以轻松解决了!
谢谢大家观看我的博客。如果有不明白的地方,或者是文中有错误的地方,欢迎指出,谢谢!如果大家喜欢我的博客,也可以给我点点赞。你们的点赞就是我的动力!