力扣热题100 - 链表:随机链表的复制

本题主要考验代码能力

题目描述:

题号:138

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

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

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

返回复制链表的头节点。

图片

 

解题思路:

思路一:先原地复制,后拆分

  1. 原地复制节点:

    • 遍历原链表,对于每个节点,将其next指针指向一个新创建的节点,该新节点的值与当前节点相同。

    • 然后,将这个新节点的next指针指向原节点的下一个节点,从而在原链表中每个节点后面都插入了一个复制的新节点。

  2. 复制随机指针:

    • 再次遍历修改后的链表,这次遍历时,对于每个原节点(我们可以通过跳过一个节点来访问它们),我们设置其后面复制节点(即新节点)的random指针。

    • 如果原节点的random指针不为空,则新节点的random指针指向原节点random指针指向的节点的下一个节点(即random指针对应节点的复制节点)。

  3. 拆分链表:

    • 最后,我们需要将原链表和复制后的新链表分离开来。首先,我们记录下原链表头节点的下一个节点,这将是新链表的头节点。

    • 然后,再次遍历链表,这次我们同时更新原链表和新链表的next指针,使它们各自独立成链。对于原链表,我们跳过每个复制节点来连接原节点;对于新链表,我们做类似的操作。

  4. 返回新链表头节点:

    • 遍历完成后,我们就得到了两个独立的链表:原链表和复制后的新链表。我们返回之前记录下的新链表的头节点。

时间复杂度:O(N)

空间复杂度:O(1)

C++


// C++
/*
// 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 == nullptr) {
            return nullptr;
        }
        // 复制节点
        Node *cur = head;
        while(cur) {
            Node *temp = cur->next;
            cur->next = new Node(cur->val);
            cur->next->next = temp;
            cur = temp;
        }

        // 复制随机指针
        cur = head;
        while(cur) {
            Node *temp = cur->next;
            temp->random = cur->random == nullptr ? nullptr : cur->random->next;
            cur = cur->next->next;
        }

        // 拆分
        // A1->A2->B1->B2->C1->C2
        Node *newHead = head->next;
        cur = head;
        Node *newCur = head->next;
        while(cur) {
            cur->next = cur->next->next;
            cur = cur->next;
            newCur->next = newCur->next == nullptr ? nullptr : newCur->next->next;
            newCur = newCur->next;
        } 

        return newHead;
    }
};

go


// go
/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Next *Node
 *     Random *Node
 * }
 */

func copyRandomList(head *Node) *Node {
    if head == nil {
        return nil
    }
    // 复制节点
    cur := head
    for cur != nil {
        next := cur.Next
        cur.Next = &Node{Val: cur.Val}
        cur.Next.Next = next
        cur = next
    }

    // 复制随机指针
    cur = head
    for cur != nil {
        curNew := cur.Next
        if cur.Random != nil {
            curNew.Random = cur.Random.Next
        }
        cur = cur.Next.Next
    }

    // 拆分
    // A1->A2->B1->B2->C1->C2
    newHead := head.Next
    cur = head
    curNew := head.Next
    for cur != nil {
        cur.Next = cur.Next.Next
        cur = cur.Next
        if curNew.Next != nil {
            curNew.Next = curNew.Next.Next
        }
        curNew = curNew.Next
    }

    return newHead
}

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值