问题:
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
思路:
方法一:最先想到的办法是借助hash保存旧结点到新结点的映射。这样在填充新节点随机结点指针字段的时候能直接映射过去。该方法用了辅助空间O(n)。
/**
* 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:
// Time complexity : O(n)
// Space complexity : O(n)
RandomListNode* copyRandomList(RandomListNode *head)
{
if(head == NULL)
return NULL;
map<RandomListNode*, RandomListNode*> m;
// Build new list
// 先建首节点,每个节点都复制label和random,并添加旧地址到新地址的映射
RandomListNode *newlist = new RandomListNode(head->label);
newlist->random = head->random;
m.insert(pair<RandomListNode*, RandomListNode*>(head, newlist));
RandomListNode *p = head->next, *q = newlist;
while(p != NULL)
{
q->next = new RandomListNode(p->label);
q = q->next;
q->random = p->random;
m.insert(pair<RandomListNode*, RandomListNode*>(p, q));
p = p->next;
}
// Fill rand-link
q = newlist;
while(q != NULL)
{
q->random = m[q->random];
q = q->next;
}
return newlist;
}
};
方法二:不使用辅助空间的高效方法。三步走:1、把复制的每个新节点插入到旧结点的后面。2、这样在填写随机指针字段的时候可以直接查看旧结点的后面。3、新旧链表拆解开。旧结点要有良心的恢复成原样。
// Time complexity : O(n)
// Space complexity : O(1)
RandomListNode* copyRandomList(RandomListNode *head)
{
if(head == NULL)
return NULL;
RandomListNode *p ,*q;
//Step one: 插入新节点
p = head;
while(p != NULL)
{
q = new RandomListNode(p->label);
q->next = p->next;
p->next = q;
p = p->next->next; //右移别忘记
}
//Step two: 填写随机字段
p = head;
while(p != NULL)
{
if(p->random == NULL) //随机指针可能是NULL
p->next->random = NULL;
else
p->next->random = p->random->next;
p = p->next->next;
}
//Step three: 拆解链表,恢复原链表,拆接出新链表
p = head;
q = head->next;
RandomListNode * newlist = q;
while(q->next != NULL)
{
p->next = q->next;
q->next = q->next->next;
p = p->next;
q = q->next;
}
p->next = NULL;
return newlist;
}