题目
(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/