剑指Offer第35题复杂链表的复制 Java实现剑指Offer两种思路,HashMap方法和无需辅助空间方法

面试题35. 复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。
在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

解题思路:这个复制过程可以分成两步:第一步是复制原始链表上的每个节点,并用next指针相连; 第二步是设置每个节点的random指针。
第一步的实现较为简单,第二步,对于一个n个节点的链表,定位每个节点的random 指针都需要从链表头节点开始经过O(n)步才能找到,因此这种方法总的时间复杂度是O(n^2)
为了降低第二步的时间复杂度,下面有两种解决方案:

解决方案一:对应推荐解法一
还是分为两步:第一步仍然是将原始链表上的每个节点 N复制为N’,然后把这些创建出来的节点N’连接起来。
同时我们把<N,N’>的配对信息放到一个HashMap<Node,Node> map中;
第二步设置每个节点的random指针:如果在原始链表中节点 N的random指针指向节点 S,那么在复制链表中,对应的 N’节点应该指向 S’。

由于有了map,我们可以用O(1)的时间根据 S找到 S’;
这种解决方案相当于用空间换时间。对于有n个节点的链表,我们需要一个大小为O(n)的HashMap,
也就是说我们以O(n)的空间消耗把时间复杂度由O(n^2)降低至O(n).
Java实现上述思路代码如下:

 //推荐双百方法一:指针版  需要一个辅助的map,下面还有一个调用方法的版本,不需要额外创建指针
   Map<Node,Node> map1 = new HashMap<>();//map前面保存原链表节点,后面保存复制节点,则可通过前面的key直接找到后面的value
   public Node copyRandomList(Node head) {
       if(head==null) return null;
       Node res = new Node(head.val);
       map1.put(head,res);
       Node tmpR = res;
       Node tmpH = head;
       while(tmpH.next!=null){
           tmpR.next = new Node(tmpH.next.val);
           tmpR=tmpR.next;
           tmpH=tmpH.next;
           map1.put(tmpH,tmpR);
       }
       tmpR = res; 
       tmpH = head;
       while(tmpH!=null){
           if(tmpH.random!=null) tmpR.random = map1.get(tmpH.random);
           tmpH = tmpH.next;
           tmpR = tmpR.next; 
       }
       return res; 
   }

也可以通过调用外部方法省去两个指针,Java代码如下:

    Map<Node,Node> map3= new HashMap<>();
    public Node copyRandomList3(Node head) {
        if(head==null) return null;
        Node res = new Node(head.val); 
        map3.put(head,res);
        copy3(head,res);
        randomAdd3(head,res);
        return res;
    }
    public void copy3(Node head,Node res){
        while(head.next!=null){
            res.next = new Node(head.next.val);
            res = res.next;
            head = head.next;
            map3.put(head,res);
        }
    }
    public void randomAdd3(Node head,Node res){
        while(head!=null){
            if(head.random!=null) res.random = map3.get(head.random);
            head= head.next;
            res= res.next; 
        }
    }

解决方案二:在不使用辅助空间的情况下实现O(n)的时问效率。对应推荐解法二
第一步:复制节点:将原始链表的任意节点 N复制为新节点N’,再把N’连接到 N的后面。
即如果原始链表为A->B->C->D 则复制过后为A->A’->B->B’->C->C’->D->D’
第二步:确定每个新增节点N’的random指针指向
显然,如果原始链表上的节点 N 的random指针指向节点S,则它对应的复制节点N’的random指针指向节点S的复制节点S’,也就是当前节点S的下一个节点。
第三步:把这个长链表拆分成两个链表,把奇数位置的节点连接起来就是原始链表,把偶数位置的节点连接起来就是复制出来的链表。
注意,此时在得到复制链表的同时,不要忘记将原有链表进行复原。
对应思路的实现代码如下:

//推荐双百方法二:不需要辅助空间,但是需要额外将两个链表进行拆分,但理解难度较大
    public Node copyRandomList2(Node head) {
        if(head==null) return null;
        copy2(head);
        randomAdd2(head);
        return build2(head);
    }
    public void copy2(Node head){
         while(head!=null){
            Node copy = new Node(head.val);
            copy.next = head.next;
            head.next =copy;
            head = copy.next;
        }
    }
    public void randomAdd2(Node head){
        while(head!=null){
           if(head.random!=null) head.next.random = head.random.next;
           head=head.next.next; 
        }
    }
    public Node build2(Node head){
        //将链表拆成两个,注意要恢复原有的链表
        Node res = head.next;
        Node tmp = res;

        head.next = head.next.next;//这一步不可缺少,保证第一个复制节点对N N'时的分离操作
        head = head.next;
        while(head!=null){
            tmp.next = head.next;
            head.next = head.next.next;
            tmp=tmp.next;
            head = head.next;
         }
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值