复杂链表的复制

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode head)
    {
        if(head == null){
            return null;
        }
        RandomListNode l1 = head;
        RandomListNode l2 = null;
        while(l1 != null){
            l2 = new RandomListNode(l1.label);
            l2.next = l1.next;
            l1.next = l2;
            l1 = l1.next.next;
        }
        l1 = head;
        while(l1 != null){
            if(l1.random != null){
                l1.next.random = l1.random.next;
            }
            l1 = l1.next.next;
        }
        l1 = head;
        RandomListNode l2_head = l1.next;
        while(l1 != null){
            l2 = l1.next;
            l1.next = l2.next;
            if(l2.next != null){
                l2.next = l2.next.next;
            }
            l1 = l1.next;
        }
        return l2_head;
    }
}

题目

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

思路

大部分人首先想到的可能是先复制复杂指针的label和next,然后再查找random并更新。查找random又分为两种,一种是每次都从头查找,时间复杂度为O(n^2);另一种是空间换时间,复制label和next的同时建立一个hash表来存放新旧复杂指针的对应关系,所以后续只需一步就能找到random,算法时间复杂度为O(n)。

我们这里将复杂链表的复制过程分解为三个步骤。在写代码的时候我们每一步定义一个函数,这样每个函数完成一个功能,整个过程的逻辑也就非常清晰明了了。

我们这里采用三步:

第一步:复制复杂指针的label和next。但是这次我们把复制的结点跟在元结点后面,而不是直接创建新的链表;

第二步:设置复制出来的结点的random。因为新旧结点是前后对应关系,所以也是一步就能找到random;

第三步:拆分链表。奇数是原链表,偶数是复制的链表。

有图思路更清晰:

hashmap方式

 

public Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }
    Map<Node, Node> map = new HashMap<>();
    Node node = head;
    while (node != null){
        map.put(node, new Node(node.val, null, null));
        node = node.next;
    }
    node = head;
    while (node != null){
        map.get(node).next = map.get(node.next);
        map.get(node).random = map.get(node.random);
        node = node.next;
    }
    return map.get(head);
}

尾复制链表

public static Node copyRandomList(Node head) {
    if (head == null) {
        return null;
    }
    Node l1 = head;
    Node l2 = null;
    //生成所有的节点,并且分别插入到原有节点的后边
    while (l1 != null) {
        l2 = new Node();
        l2.val = l1.val;
        l2.next = l1.next;
        l1.next = l2;
        l1 = l1.next.next;
    }
    //更新插入节点的 random
    l1 = head;
    while (l1 != null) {
        if (l1.random != null) {
            l1.next.random = l1.random.next;
        }
        l1 = l1.next.next;
    }

    l1 = head;
    Node l2_head = l1.next;
    //将新旧节点分离开来
    while (l1 != null) {
        l2 = l1.next;
        l1.next = l2.next;
        if (l2.next != null) {
            l2.next = l2.next.next;
        }
        l1 = l1.next;
    }
    return l2_head;
}

主函数

public static void main(String[] args) {
    Node head = null;
    head = new Node();
    head.val = 1;
    head.next = new Node();
    head.next.val = 2;
    head.next.next = new Node();
    head.next.next.val =3;
    head.next.next.next = new Node();
    head.next.next.next.val =4;
    head.next.next.next.next = new Node();
    head.next.next.next.next.val = 5;
    head.next.next.next.next.next = new Node();
    head.next.next.next.next.next.val = 6;
    head.random = head.next.next.next.next.next; // 1 -> 6
    head.next.random = head.next.next.next.next.next; // 2 -> 6
    head.next.next.random = head.next.next.next.next; // 3 -> 5
    head.next.next.next.random = head.next.next; // 4 -> 3
    head.next.next.next.next.random = null; // 5 -> null
    head.next.next.next.next.next.random = head.next.next.next; // 6 -> 4
    Node res = copyRandomList(head);
    printRandLinkedList(res);
}

打印函数

public static void printRandLinkedList(Node head) {
    Node cur = head;
    System.out.print("order:");
    while (cur != null) {
        System.out.print(cur.val + " ");
        cur = cur.next;
    }

    cur = head;
    System.out.println();
    System.out.print("random:");
    while (cur != null) {
        System.out.print(cur.random == null ? "- " : cur.random.val + " ");
        cur = cur.next;
    }
    System.out.println();
}