复制带随机指针的单链表

复制带随机指针的链表

题目

(1) 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random,该指针可以指向链表中的任何节点或空节点。
(2) 构造这个链表的深拷贝。 深拷贝应该正好由 n 个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
(3) 例如,原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
(4) 返回复制链表的头节点。
(5) 说明:用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
  • 你的代码只接受原链表的头节点 head 作为传入参数。

示例如下:
在这里插入图片描述
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

解决思路

  • 第一步:在原链表的每个节点(设为A)后面插入该节点的副本(设为A1,此时复制节点A1的random指针和A的random指针都指向同一个节点,假设都指向B节点)。
  • 第二步:改变复制节点A1的random指针的指向,让复制节点的random指针指向对应的复制节点,如A1的random指向B1。
  • 第三步:拆分链表,将所有的原节点拆分为一条链表,将所有的复制节点拆分为一条链表。
  • 上述步骤如下图所示:
    在这里插入图片描述

代码

  • C++代码
# include <stdio.h>

class Node {
public:
    int val;
    Node *next;
    Node *random;

    Node(int _val=0) {
        val = _val;
        next = nullptr;
        random = nullptr;
    }
};


class Solution {
public:
    Node *copyRandomList(Node* head) {
        if (nullptr == head) {
            return head;
        }

        Node *h = head;
        Node *new_head;     // 指向复制链表的头节点
        Node *q;            // 每个节点的副本

        // 第一步:在每个节点的后面插入自身的副本
        while (h) {
            q = new Node(h->val);
            q->next = h->next;
            q->random = h->random;
            h->next = q;
            h = h->next->next;
        }
        
        // 第二步:改变副本节点的random指针的指向,让副本节点的random指向对应节点的副本节点
        h = head->next;     // 指向第一个节点的副本
        while (h) {
            if (h->random) {
                h->random = h->random->next;
            }

            // 让p指向下一个副本节点
            (h = h->next) && (h = h->next);     // 与下面的代码等价
            // h = h->next;
            // if (h) {
            //     h = h->next;
            // }
        }
        
        // 第三步:拆分出两个链表
        new_head = head->next;
        h = head;
        while (h) {
            q = h->next;
            h->next = q->next;
            if (h->next) {
                q->next = h->next->next;
            }
            h = h->next;
        }
        return new_head;
    }
};


int main() {
    Node *a = new Node(7);
    Node *b = new Node(13);
    Node *c = new Node(11);
    Node *d = new Node(10);
    Node *e = new Node(1);

    a->next = b;
    b->next = c;
    c->next = d;
    d->next = e;

    a->random = nullptr;
    b->random = a;
    c->random = e;
    d->random = c;
    e->random = a;

    Solution *solution = new Solution();
    Node *ret = solution->copyRandomList(a);
    
    if (ret == a) {
        printf("err!");
        return 0;
    }

    while (ret) {
        int val = ret->val;
        if (ret->random) {
            printf("[%d,%d] ", ret->val, ret->random->val);
        } else {
            printf("[%d,%s] ", ret->val, NULL);
        }
        ret = ret->next;
    }

    return 0;
}
  • python代码
# -*- coding: utf-8 -*-

class Node:
    def __init__(self, val: 'int' = 0, next: 'Node' = None, random: 'Node' = None):
        self.val = val
        self.next = next
        self.random = random


class Solution:
    def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
        if not head:
            return head

        h: Node = head
        q: Node             # 每个节点的副本
        new_head: Node      # 指向复制链表的头节点

        # 第一步:在每个节点的后面插入自身的副本
        while h:
            q = Node(h.val)
            q.next = h.next
            q.random = h.random
            h.next = q
            h = q.next

        # 第二步:改变副本节点的random指针的指向,让副本节点的random指向对应节点的副本节点
        h = head.next       # 指向第一个节点的副本
        while h:
            if h.random:
                h.random = h.random.next

            # 让p指向下一个副本节点
            h = h.next
            if h:
                h = h.next

        # 第三步:拆分出两个链表
        new_head = head.next
        h = head
        while h:
            q = h.next
            h.next = q.next
            if h.next:
                q.next = h.next.next
            h = h.next

        return new_head


def main():
    a: Node = Node(7)
    b: Node = Node(13)
    c: Node = Node(11)
    d: Node = Node(10)
    e: Node = Node(1)

    a.next = b
    b.next = c
    c.next = d
    d.next = e

    a.random = None
    b.random = a
    c.random = e
    d.random = c
    e.random = a

    solution: Solution = Solution()
    ret: Node = solution.copyRandomList(a)
    if ret == a:
        print("the same head")
        return False

    while ret:
        if ret.random:
            print('[%d, %d]' % (ret.val, ret.random.val), end=' ')
        else:
            print('[%d, %s]' % (ret.val, None), end=' ')
        ret = ret.next


if __name__ == "__main__":
    main()

说明

  • 对应LeetCode第138题。
  • 链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值