Loading

Medium | 剑指 Offer 35. 复杂链表的复制

剑指 Offer 35. 复杂链表的复制

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

示例 1:

img
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

img
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

img

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

提示:

  • -10000 <= Node.val <= 10000
  • Node.random 为空(null)或指向链表中的节点。
  • 节点数目不超过 1000 。

方法一:HashMap

一个简单的想法就是分为两步走, 先把next指针全部指向相应的克隆的节点, 然后再处理random指针

遍历原链表节点并赋值节点, 将前驱节点的指针修改为克隆的节点。这样一趟遍历完成后, 所有next指针全部指向正确的位置了。那么random指针的指向问题如何处理呢?

  1. 简单来想, 我可以设置一个计数器只是某个链表的节点是第几个节点。这样我依据可以通过遍历链表的方式找到原链表节点random指针指向链表的第一个元素。然后依据此值,遍历克隆链表, 找到这个节点, 并相应的给克隆节点的random指针赋值即可。

  2. 上述的思路其实非常繁琐, 每找一个random都要遍历一遍原链表和克隆链表, N个节点要遍历N次。就是为了找节点之间的对应关系, 面对这样的问题, 我们只需要在第一次克隆链表, 赋值Next指针的过程中, 把原节点和克隆节点的对应关系保存到HaspMap里, 就能通过原节点的Random指针, 迅速找到可能节点的Random指向元素的指针了。比如对应关系分别是(A1,A2), (B1,B2), (C1,C2)... (Z1,Z2)。如果A1的random指针指向Z1, 那么我立马就可以根据节点的对应关系, 让克隆节点A2的random指针指向Z2, 这样random指针的复制也只需要遍历一遍数据就可以全部完成了。

方法二

其实方法一的时间复杂度已经优化不错了, 但是空间复杂度还有可优化的空间。

这种方法把步骤分为3步:

  1. 复制节点, 并且将复制的节点放在原节点的后面, 先用next指针串起来

  2. 遍历链表, 赋值random指针。因为克隆节点的random指向的节点可以通过原节点的random指针的next指针直接得到。所以这一步的可以直接赋值。

  3. 然后将链表的Next指针还原, 并拆分成两个链表

其实这种方法的核心思想是和第一种HashMap的思想是一致的, 那就是保存原节点和克隆节点之间的对应关系, 方法一使用额外的HashMap来保存对应关系, 而这种方法,使用next指针保存对应关系。

public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }
    cloneNode(head);
    connectSibling(head);
    return splitLinkedList(head);
}
// 克隆链表, 并将克隆的节点, 修改原节点的next指向克隆节点
public void cloneNode(Node head) {
    Node p = head;
    while(p != null) {
        Node pClone = new Node(p.val);
        Node nextNode = p.next;
        p.next = pClone;
        pClone.next = nextNode;
        p = nextNode;
    }
}
// 连接random指针
public void connectSibling(Node head) {
    Node p = head;
    while(p != null) {
        // 这里要注意random指针可能为空, 要额外判断
        p.next.random = ((p.random != null) ? p.random.next : null);
        p = p.next.next;
    }
}
// 将链表还原回来 next指针重新指向下一个节点
public Node splitLinkedList(Node head) {
    Node headClone = head.next;
    Node p = head;
    while(p != null) {
        Node nextNode = p.next.next;
        Node pClone = p.next;
        p.next = nextNode;
        pClone.next = nextNode != null ? nextNode.next : null;
        p = nextNode;
    }
    return headClone;
}
posted @ 2021-01-15 13:45  反身而诚、  阅读(44)  评论(0编辑  收藏  举报