后插节点解答
实现目标,要将复杂问题拆解为较为解决的较小问题进行逐步解决。可以将要复制的链表分为节点插入到每个原链表的节点后面,如图所示:
1.插入复制节点
想要实现如图所示效果,首先用malloc创建节点,然后后插入每个原节点的后面,这里要注意的是,应该先改变插入节点的next指针,防止因为覆盖导致原指针的next指针无法被找到。代码如下:
struct Node* cur = head;
while(cur)
{
//创建节点
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
copy->next = cur->next;
cur->next = copy;
cur = copy->next;//通过赋值让cur继续往后走
}
结束条件以cur为主体确定:cur走到空停止。
2.控制random
通过后插节点解答的优越性就体现出来了,将难以寻找的random节点通过后插copy节点实现新的copy节点的random指针在原节点的random指针的下一个位置。代码实现如下:
cur = head;
while(cur)
{
struct Node* copy = cur->next;;
if(cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;//此解法的优势所在
}
cur = copy->next;//cur往后走
}
3.将效果实现好的copy从原链表中拆解为独立的新链表(恢复原链表)
在这里我们为了防止覆盖要多创建一个next指针存放节点位置,并定义头节点与尾指针来进行尾插的实现。循环的主体仍是cur。代码实现如下:
cur = head;
struct Node* copytail = NULL, *copyhead = NULL;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
if(copytail == NULL)
{
copyhead = copytail = copy;
}
else
{
copytail->next = copy;
copytail = copytail->next;
}
cur->next = next;//恢复原链表
cur = next;
}
return copyhead;
像这样,通过化繁为简的方法将复杂的问题分为三块较为简单的函数,就可以解决问题了。
最后,我们将代码整合起来,即为:
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head)
{
struct Node* cur = head;
while(cur)
{
//创建节点
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
copy->next = cur->next;
cur->next = copy;
cur = copy->next;//copy往后走
}
//控制random
cur = head;
while(cur)
{
struct Node* copy = cur->next;;
if(cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;//此解法的优势所在
}
cur = copy->next;//cur往后走
}
//将copy节点尾插到新链表中
cur = head;
struct Node* copytail = NULL, *copyhead = NULL;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
if(copytail == NULL)
{
copyhead = copytail = copy;
}
else
{
copytail->next = copy;
copytail = copytail->next;
}
cur->next = next;//恢复原链表
cur = next;
}
return copyhead;
}