三次优化剑指 Offer 35. 复杂链表的复制

Posted 来老铁干了这碗代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三次优化剑指 Offer 35. 复杂链表的复制相关的知识,希望对你有一定的参考价值。

立志用最少代码做最高效的表达


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

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

示例 2:
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:
输入: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

思路:以map将新节点和旧节点映射起来,进而建立链表。
步骤
1、完成以next指针为串联的链表的建立,并建立HashMap映射
2、完成random的链表建设
缺点:冗余

class Solution {
  public Node copyRandomList(Node head) {
        /**
         1、根据next完成链表的复制,同时建立hash表映射
         2、根据hash表的映射进行random的指向
         */
        if(head == null) return null;           //头结点为空的情况

        Map<Node, Node> map = new HashMap<>();   // 建立Map映射,映射原节点和被复制的节点
        Node copyListHead = new Node(head.val); // 复制链表的头结点
        map.put(head, copyListHead);            // 建立头结点间的映射

        Node oldTmpList1 = head.next;               // 原链表的临时遍历节点
        Node copyTmpList1 = copyListHead;           // 复制链表的临时遍历节点1
        // 1、完成next的复制
        while(oldTmpList1 != null) {
            Node newTmpList1 = new Node(oldTmpList1.val);    // 赋值链表的临时遍历节点2(因为建表需要两个节点)

            map.put(oldTmpList1, newTmpList1);    // 建立映射

            copyTmpList1.next = newTmpList1;              // 建表过程
            copyTmpList1 = newTmpList1;
            oldTmpList1 = oldTmpList1.next;

        }

        // 2、完成random的复制
        Node oldTmpList2 = head;
        Node newTmpList2 = copyListHead;           // 复制链表的临时遍历节点1
        while(oldTmpList2 != null) {
            if(oldTmpList2.random != null) {
                newTmpList2.random = map.get(oldTmpList2.random);
            }
            oldTmpList2 = oldTmpList2.next;
            newTmpList2 = newTmpList2.next;
        }
        return copyListHead;
    }
}

思路2:对思路1的优化

拆分成两步:
1、将新旧节点映射起来
2、进行next和random的链表建立
时间复杂度:O(n) 、 空间复杂度:O(n)

class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;           //头结点为空的情况

        Map<Node, Node>map = new HashMap<>();   // 建立Map映射,映射原节点和被复制的节点
        Node copyListHead = new Node(head.val); // 复制链表的头结点

        // 1、进行Map映射
        for(Node tmp = head; tmp != null; tmp = tmp.next) {
            if(tmp == head) map.put(head, copyListHead);    // 头结点单独判断
            else map.put(tmp, new Node(tmp.val));           // 建立映射
        }

        // 2、完成next的复制和random的复制
        for(Node tmp = head; tmp != null; tmp = tmp.next) {
            map.get(tmp).next = map.get(tmp.next);
            map.get(tmp).random = map.get(tmp.random);
        }
        return copyListHead;
    }
}

代码3:原地复制

思路:在原链表的每个节点后都构建一个新节点,将原来形如A->B->C的链表改为A->A'->B->B'->C->C',然后拆分两链表即可。

步骤
1、复制各个节点,构建拼接链表
2、构建各个新节点的random指针
3、拆分成两个链表

注意:本题的题意是深拷贝,即不能更改原链表。

class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;
        Node cur = head;
        // 1. 复制各节点,并构建拼接链表
        while(cur != null) {
            Node tmp = new Node(cur.val);
            tmp.next = cur.next;
            cur.next = tmp;
            cur = tmp.next;
        }
        // 2. 构建各新节点的 random 指向
        cur = head;
        while(cur != null) {
            if(cur.random != null)
                cur.next.random = cur.random.next;
            cur = cur.next.next;
        }
        // 3. 拆分两链表
        cur = head.next;
        Node pre = head, res = head.next;
        while(cur.next != null) {
            pre.next = pre.next.next;
            cur.next = cur.next.next;
            pre = pre.next;
            cur = cur.next;
        }
        pre.next = null; // 单独处理原链表尾节点
        return res;      // 返回新链表头节点
    }
}

以上是关于三次优化剑指 Offer 35. 复杂链表的复制的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

剑指offer--35复杂链表的复制