Lintcode 105. 复制带随机指针的链表
题目描述:
给出一个链表,每个节点包含一个额外增加的随机指针可以指向链表中的任何节点或空的节点。返回一个深拷贝的链表。
解题思路:
1、一种实现方式是可以通过map这个数据结构存储新老节点之间的映射关系,进而深度拷贝链表。
2、这道题目的最大的挑战是用O(1)的空间实现,即常数级的额外空间实现。这时候map存的这个映射关系就是额外空间耗费了,不是O(1)的空间实现。
不用map怎么实现呢?
据说这是微软很久以前的考题,这个实现想法大部分人都想不到。
基本思路是先将原链表1->2->3->3->4->NULL
转换为 => 1->1'->2->2'->3->3'->4->4'...
也就是用原链表中节点的next存映射的新节点,用原节点next的next存原来自己的next。random节点也是从这个映射关系出发进行映射的,这样因为1’,2’…这些值是最后要返回的值,所以不算额外空间耗费,就能实现O(1)空间的目标。
小知识:
常数级的额外空间:大体是指除了传进来的值和最后return的值之外,不能有其他数量级大的空间耗费(比如你new了一个很长(n)的数组),当然了新建常数级别的几个变量还是可以的。
下面的代码给出了两种方法(使用map和 O(1)空间实现),以及两种语言(C++和Java)的实现:
(1)C++:
// 1、Map方法
/**
* Definition for singly-linked list with a random pointer.
* struct RandomListNode {
* int label;
* RandomListNode *next, *random;
* RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
/**
* @param head: The head of linked list with a random pointer.
* @return: A new head of a deep copy of the list.
*/
RandomListNode *copyRandomList(RandomListNode *head) {
map<RandomListNode*, RandomListNode*> old2new_Map;//新旧节点之间的映射关系
RandomListNode *dummy = new RandomListNode;
RandomListNode *prev = dummy;
RandomListNode *newNode;
while (nullptr != head) {
if (old2new_Map.count(head)) {
newNode = old2new_Map[head];
} else {
RandomListNode *node = new RandomListNode(head->label);
old2new_Map[head] = node;
newNode = node;
}
prev->next = newNode;
if (nullptr != head->random) {
if (old2new_Map.count(head->random)) {
newNode->random = old2new_Map[head->random];
} else {
RandomListNode *randNode = new RandomListNode(head->random->label);
old2new_Map[head->random] = randNode;
newNode->random = randNode;
}
} else {
newNode->random = nullptr;
}
prev = newNode;
head = head->next;
}
return dummy->next;
}
};
// 2、O(1)空间实现
/**
* Definition for singly-linked list with a random pointer.
* struct RandomListNode {
* int label;
* RandomListNode *next, *random;
* RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
/**
* @param head: The head of linked list with a random pointer.
* @return: A new head of a deep copy of the list.
*/
RandomListNode *copyRandomList(RandomListNode *head) {
// write your code here
if(!head)
return head;
for(auto p=head;p;){
auto n=new RandomListNode(p->label);
n->next=p->next;
p->next=n;
p=n->next;
}
for(auto p=head;p;p=p->next->next)
if(p->random)
p->next->random=p->random->next;
RandomListNode* nh=head->next,*tail=nh;
while(tail){
head=head->next=head->next->next;
tail=tail->next=head?head->next:nullptr;
}
return nh;
}
};
(2)Java:
//HashMap version
public class Solution {
public RandomListNode copyRandomList(RandomListNode head) {
if (head == null) {
return null;
}
HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
RandomListNode dummy = new RandomListNode(0);
RandomListNode pre = dummy, newNode;
while (head != null) {
if (map.containsKey(head)) {
newNode = map.get(head);
} else {
newNode = new RandomListNode(head.label);
map.put(head, newNode);
}
pre.next = newNode;
if (head.random != null) {
if (map.containsKey(head.random)) {
newNode.random = map.get(head.random);
} else {
newNode.random = new RandomListNode(head.random.label);
map.put(head.random, newNode.random);
}
}
pre = newNode;
head = head.next;
}
return dummy.next;
}
}
/*第一遍扫的时候巧妙运用next指针, 开始数组是1->2->3->4 。 然后扫描过程中 先建立copy节点 1->1`->2->2`->3->3`->4->4`, 然后第二遍copy的时候去建立边的copy, 拆分节点, 一边扫描一边拆成两个链表,这里用到两个dummy node。第一个链表变回 1->2->3 , 然后第二变成 1`->2`->3` */
//No HashMap version
public class Solution {
private void copyNext(RandomListNode head) {
while (head != null) {
RandomListNode newNode = new RandomListNode(head.label);
newNode.random = head.random;
newNode.next = head.next;
head.next = newNode;
head = head.next.next;
}
}
private void copyRandom(RandomListNode head) {
while (head != null) {
if (head.next.random != null) {
head.next.random = head.random.next;
}
head = head.next.next;
}
}
private RandomListNode splitList(RandomListNode head) {
RandomListNode newHead = head.next;
while (head != null) {
RandomListNode temp = head.next;
head.next = temp.next;
head = head.next;
if (temp.next != null) {
temp.next = temp.next.next;
}
}
return newHead;
}
public RandomListNode copyRandomList(RandomListNode head) {
if (head == null) {
return null;
}
copyNext(head);
copyRandom(head);
return splitList(head);
}
}