Copy List with Random Pointer (LeetCode)

题目:

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。现在要求深度拷贝一个这样的链表。即所有节点的label都和原链表一样,next指针和random指针所指的节点也都和原链表的一一对应。另外,在做题的过程中发现,O(n^2)的算法,系统会提示TimeLimitedExceed错误,必须要找出更快的算法才可以。


思路

本题的难点在于,如何找到新链表中节点random指针所对应的节点。以两个链表的头结点head和head2为例,已知head.random,怎么在新链表中找到head2.random对应的节点?最好的办法,可能就是判断head.random指向的节点和head结点的相对距离,来确定head2.random和head2相对距离。

最开始,想到一个办法是同步遍历。让traverse1从head开始,traverse2从head2开始,同步遍历(traverse1走一步,traverse2也走一步)。当traverse1走到head.random对应的节点时,traverse2也就走到了head2.random对应的节点,把traverse2对应的节点赋给head2.random即可。对于head2之外的节点,可以使用同样的方法。但是,这个算法是O(n^2),在LeetCode中没有通过,提示TimeLimitedExceed错误。(这一段代码贴在本文最后方)

于是想,能不能不用遍历,就能找到head2.random对应节点和head2的相对距离呢?是可以的。可以通过这些指针所在地址来确定位置。例如(&head.random-&head), 我们就知道head.random和head的地址距离差。然后&head2.random = &head2 +(&head.random-&head) 就能确定号head2.random对应的位置。这是个初步的想法,不确定一定就能实现。关键是,java中禁止读取指针的地址,所以这个办法或许可以在c中实现,在java中是绝对不能的。

最后想到的解决办法是,建立一个Hash表。让原链表中每个节点分别作为key,让新链表中每个节点分别对应作为value,例如<head,head2>就是Hash表中的一个pair。这样,当我们读取到head.random节点的时候,可以直接通过Hash表得到head2.random对应的那个节点了。具体算法如下:

  1. 先浅度复制一个这样的链表
    • 复制每个节点,以及next指针对应的连接
    • 并且,让原链表中节点traverse1和新链表中节点traverse2成为一个Hash对,放入Hash表map中
    • 节点的random指针都先指向null。
    • 后面的步骤即安排每个节点的random指针指向哪个节点
  2. traverse1和traverse2分别为两个链表中对应的节点
    • traverse1.random如果为null,直接让traverse2.random也为null,退出进行下一个节点
    • 通过map.get(traverse1.random)找到traverse1.random对应节点在新链表中相对应的节点,即是traverse2.random应该指向的节点




注意点:

  1. 还想过,把两个链表中各节点放到两个数组中,即array1[]和array2[]中。通过找到traverse1.random在array1中的index,从而在array2中找到index对应的节点,赋给traverse2.random。但是,查看API文档,Array类没有indexOf之类的方法能得到index的(系统应该可以通过地址判断出index),所以只能通过遍历数组得到index。这样,算法还是O(n^2),没有改进。作罢。




代码:(Hash表方法)

/**
 * Definition for singly-linked list with a random pointer.
 * class RandomListNode {
 *     int label;
 *     RandomListNode next, random;
 *     RandomListNode(int x) { this.label = x; }
 * };
 */
import java.util.HashMap;
public class Solution {
    public RandomListNode copyRandomList(RandomListNode head) {
        if (head == null){
            return null;
        }
        RandomListNode head2, traverse1, traverse2;
        HashMap<RandomListNode,RandomListNode> map = new HashMap<RandomListNode,RandomListNode>();
        head2 = new RandomListNode(head.label);
        traverse1 = head. next;
        traverse2 = head2;
        map.put(head,head2);
        while (traverse1 != null){
            traverse2.next = new RandomListNode(traverse1.label);
            traverse2 = traverse2.next;
            map.put(traverse1,traverse2);
            traverse1 = traverse1.next;
        }
        traverse1 = head;
        traverse2 = head2;
        while(traverse1 != null){
            if(traverse1.random == null){
                traverse2.random = null;
                traverse1 = traverse1.next;
                traverse2 = traverse2.next;
                continue;
            }
            traverse2.random = map.get(traverse1.random);
            traverse1 = traverse1.next;
            traverse2 = traverse2.next;
        }
        return head2;
    }
}


代码:(同步遍历方法)

这个代码在LeetCode上又TimeLimitedExceed错误,但是逻辑上应该可以运行。

/**
 * Definition for singly-linked list with a random pointer.
 * class RandomListNode {
 *     int label;
 *     RandomListNode next, random;
 *     RandomListNode(int x) { this.label = x; }
 * };
 */
import java.util.*;
public class Solution {
    public RandomListNode copyRandomList(RandomListNode head) {
        if (head == null){
            return null;
        }
        RandomListNode head2, traverse1, traverse2;
        List<RandomListNode> arrayList1 = new ArrayList<RandomListNode>();
        List<RandomListNode> arrayList2 = new ArrayList<RandomListNode>();
        head2 = new RandomListNode(head.label);
        traverse1 = head. next;
        traverse2 = head2;
        arrayList1.add(head);
        arrayList2.add(head2);
        while (traverse1 != null){
            traverse2.next = new RandomListNode(traverse1.label);
            traverse2 = traverse2.next;
            arrayList1.add(traverse1);
            arrayList2.add(traverse2);
            traverse1 = traverse1.next;
        }
        traverse1 = head;
        traverse2 = head2;
        while(traverse1 != null){
            if(traverse1.random == null){
                traverse2.random = null;
                traverse1 = traverse1.next;
                traverse2 = traverse2.next;
                continue;
            }
            traverse2.random = arrayList2.get(arrayList1.indexOf(traverse1.random));
            traverse1 = traverse1.next;
            traverse2 = traverse2.next;
        }
        return head2;
    }
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值