Leetcode 138. 复制带随机指针的链表

310 阅读3分钟

原题链接: 138. 复制带随机指针的链表 - 力扣(Leetcode)

tag: 链表.

一. 题目

给你一个长度为 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 作为传入参数.

二. 题解

image.png

本题按照如下四步进行理解.

1. 在原链表的每一个节点后复制出一个相同的节点

Node* curr = head;

image.png

curr != nullptr

image.png

Node* newnode = new Node(curr->val);

image.png

newnode->next = curr->next;

image.png

curr->next = newnode;

image.png

curr = curr->next->next;

image.png

重复这一步骤.

image.png

直到 curr == nullptr.

image.png

2. 修改复制出的新节点的 random 指针的指向

Node* curr = head;

image.png

curr != nullptr

image.png

curr->random == nullptr

image.png

curr = curr->next->next;

image.png

curr != nullptr

image.png

curr->random != nullptr

image.png

修改拷贝节点 random 指针的指向.

curr->next->random = curr->random->next;

image.png

curr = curr->next->next;

image.png

重复这一步骤.

image.png

直到 curr == nullptr.

image.png

3. 将拷贝节点逐个尾插到虚拟头节点的后面, 并对原链表中的节点与拷贝节点进行拆分

Node* dummy = new Node(0), * tail = dummy;

image.png

Node* curr = head;

image.png

curr != nullptr

image.png

Node* newnode = curr->next;

image.png

改变拷贝节点的 next 指针指向.

tail->next = newnode;

image.png

tail = tail->next;

image.png

恢复原链表节点 next 指针的指向.

curr->next = newnode->next;

image.png

curr = curr->next;

image.png

重复这一步骤, 直到 curr == nullptr.

image.png

4. 返回原链表的深拷贝

head = dummy->next;

image.png

delete dummy;

image.png

return head;

image.png

三. 复杂度分析

时间复杂度: O(N), 其中 N 是链表的长度, 我们需要遍历原链表三次.

空间复杂度: O(1).

四. 代码

/*
// 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) {
        for (Node* curr = head; curr; curr = curr->next->next) {
            Node* newnode = new Node(curr->val);
            newnode->next = curr->next;
            curr->next = newnode; 
        }    // 在原链表的每一个节点后复制出一个相同的节点

        for (Node* curr = head; curr; curr = curr->next->next) {
            if (curr->random != nullptr) {
                curr->next->random = curr->random->next;
            }
        }    // 修改复制出的新节点的 random 指针的指向
        
        Node* dummy = new Node(0), * tail = dummy;
        for (Node* curr = head; curr; curr = curr->next) {
            Node* newnode = curr->next;
            tail->next = newnode;
            tail = tail->next;
            curr->next = newnode->next;
        }    // 将拷贝节点逐个尾插到虚拟头节点的后面, 并对原链表中的节点与拷贝节点进行拆分

        head = dummy->next;    // 返回原链表的深拷贝
        delete dummy;
        return head;
    }
};