【面试题】复杂链表的复制(两种解法)

题目描述

在这里插入图片描述
在这里插入图片描述

解法一:通过哈希表辅助查找

  • 第⼀步先复制结点的值,针对原始链表上的每个结点 N,在新链表上创建对应的结点 N’ 并复制 N的值,同时我们把<N, N’>的配对信息放到⼀个哈希表中。
  • 第⼆步是复制结点的指针,把这些复制链表上新创建出来的结点⽤ next指针链接起来,并设置复制链表上每个结点的 random指向。如果在原始链表中结点 N的 random指向结点S, 那么在复制链表中, 对应的N’的 random应该指向S’。 由于有了哈希表, 我们可以⽤O(1) 的时间根据 S找到 S’。

Java

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) { // 主要构造函数需要传入节点值作为参数
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;
        HashMap<Node, Node> map = new HashMap<> (); // 创建哈希表,存放原始链表和复制链表中每个节点的配对关系
        Node cur = head;
        // 1.复制结点值
        while(cur != null) {
            Node clone = new Node(cur.val); // 创建新节点,复制老节点的值
            map.put(cur, clone); // 存储<key,value1>
            cur = cur.next;
        }
        // 2.复制结点的指向
        cur = head; // 重新回到原始链表的头结点开始顺序遍历
        while(cur != null) {
            map.get(cur).next = map.get(cur.next); //新结点 next指向同老结点的 next指向
            map.get(cur).random = map.get(cur.random); //新结点 random指向同老结点的 random指向
            cur = cur.next;
        }
        // 返回复制链表的头结点,通过原始链表的 head和哈希表查找到
        return map.get(head);
    }
}
复杂度分析

时间复杂度:O(n),遍历一遍原始链表复制新链表的节点值需要 O(n)的时间 + 再遍历一遍原始链表复制新链表的指针指向也需要 O(n)的时间,O(2*n) 即O(n)
空间复杂度:O(n),⽤空间换时间的思想,对于有 n个结点的链表需要⼀个大小为O(n) 的哈希表。

解法二:不用辅助空间实现O(n)的查找效率

  • 第⼀步仍然是根据原始链表的每个结点 N创建对应的 N’。不过,这次我们把复制后的新节点 N’连接在 N的后面
  • 第二步复制原始链表上节点的 random 指针,如果原始链表上的结点 N的 random指针指向结点 S, 则它对应的复制结点 N’的 random指针指向 S的下⼀结点 S’。
  • 第三步把这个长链表拆分成两个链表: 把奇数位置的结点⽤ Next链接起来就是原始链表, 把偶数位置的结点⽤ Next 链接起来就是复制出来的链表。

在这里插入图片描述

Java

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;
        copyNode(head);
        copyRandomDirect(head);
        return reList(head);
    }
    // 复制链表
    public void copyNode(Node head) {
        Node cur = head;
        while(cur != null) {
            Node clone = new Node(cur.val); // 创建新节点并复制结点值
            // 新节点接在老节点后面
            clone.next = cur.next; 
            cur.next = clone; 
            cur = clone.next; 
        }
    }
    // 复制原始链表中的随机指针
    public void copyRandomDirect(Node head) {
        Node cur = head;
        while(cur != null) {
            Node clone = cur.next;
            if(cur.random != null)
                clone.random = cur.random.next;
            cur = clone.next;
        }
    }
    // 重新连接链表,把原始链表和新链表拆分出来
    public Node reList(Node head) {
        Node cloneNode = head.next;
        Node cloneHead = head.next; // 记录新链表的头结点
        head.next = cloneNode.next;
        head = head.next;
        while(head != null) {
            cloneNode.next = head.next;
            cloneNode = cloneNode.next;
            head.next = cloneNode.next;
            head = head.next;
        }
        return cloneHead;
    }
}
复杂度分析

时间复杂度:O(n),三趟顺序遍历分别需要 O(n)的时间,O(3*n) 即O(n)
空间复杂度:O(1)

参考

https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/solution/fu-za-lian-biao-de-fu-zhi-jian-dan-yi-dong-de-san-/

图解 链表的深拷贝

https://leetcode-cn.com/problems/copy-list-with-random-pointer/solution/fu-zhi-dai-sui-ji-zhi-zhen-de-lian-biao-by-leetcod/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值