138. 随机链表的复制【 力扣(LeetCode) 】

一、题目描述

【总结一句话:重新弄出一个完全一样链表

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。

你的代码 只 接受原链表的头节点 head 作为传入参数。

二、测试用例

示例 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]]

提示:

0 <= n <= 1000
-104 <= Node.val <= 104
Node.random 为 null 或指向链表中的节点。

三、解题思路

3.1 建立旧结点-下标-新结点的映射关系

  • 定义:ans 存放答案;映射 m 和 n 用来表示 旧结点到下标的映射 和 下标到新结点的映射;
  • 第一次遍历:建立新结点,并建立 旧结点到下标的映射关系 和 下标到新结点的映射 ;
  • 第二次遍历:根据 旧结点-下标-新结点 的映射关系来确定 random

3.2 合并+拆分

  • 第一遍遍历:把链表的结点复制一份,并连接到对应节点的后面;【例如:原:a-b-c ,复制完 a-a‘-b-b’-c-c‘ 】
  • 第二遍遍历:确定复制节点的随机指针,对于复制结点来说,其随机指针应该指向原本结点的下一个;【例如:假设 c 随机指向 a ,则 c’ 随机指向 a 的下一个结点,即 a‘ 】【注意空指针的处理,空指针没有下一个】
  • 第三边遍历:拆分链表,将原本结点和复制结点拆分成两条链表。【注意最后一个结点的处理】

四、参考代码

4.1 建立旧结点-下标-新结点的映射关系

时间复杂度: O ( n ) \Omicron(n) O(n)
空间复杂度: O ( n ) \Omicron(n) O(n)

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

    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (head == NULL)
            return NULL;
        Node* ans = new Node(head->val);
        unordered_map<Node*, int> m;
        unordered_map<int, Node*> n;
        int index = 0;

        m[head] = index;
        n[index] = ans;
        index++;
        for (Node *p = ans, *q = head->next; q != NULL; p = p->next, q = q->next) {
            p->next = new Node(q->val);
            m[q] = index;
            n[index] = p->next;
            index++;
        }
        for (Node *p = ans, *q = head; p != NULL; p = p->next, q = q->next) {
            if (q->random == NULL) {
                p->random = NULL;
            } else {
                p->random = n[m[q->random]];
            }
        }

        return ans;
    }
};

4.2 合并+拆分

时间复杂度: O ( n ) \Omicron(n) O(n)
空间复杂度: O ( n ) \Omicron(n) O(n) 【复制结点的空间,空间会比 4.1 小,因为不用存储 map】

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head==NULL) return NULL;
        Node *ans=NULL;
        for(Node *p=head;p!=NULL;p=p->next->next){
            Node *t=new Node(p->val);
            t->next=p->next;
            p->next=t;
        }
        for(Node *p=head;p!=NULL;p=p->next->next){
            p->next->random=(p->random==NULL)?NULL:p->random->next;
        }
        ans=head->next;
        for(Node *p=head,*q=ans;q!=NULL;p=p->next,q=q->next){
            p->next=q->next;
            if(p->next!=NULL)
                q->next=p->next->next;
            else{
                q->next=NULL;
                break;
            }
        }

        return ans;
    }
};
  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值