链表面试题3:实现一个复杂链表的复制,复杂链表中,每个结点除了有一个pNext指针指向下一个结点外,还有一个pRandom指针指向链表中任一结点或者NULL。
-
题目分析:
-
如下图这样的复杂链表,随机指针可能指向结点前面的结点、或者结点后面的结点、也可以指向它自己或者NULL
-
第一反应可以将复制分为两步,先进行普通单链表的复制,再进行每个结点random随机指针的复制,但random指针可能指向结点的前方,这时就需要从头再遍历找到random指向的结点再进行复制,对于n个结点的链表,若每个结点random都指向前面的结点,这就意味着每个结点都需从头遍历一次,这种方法的时间复杂度为O(n^2),我们需要做进一步的优化。
-
解题思路:
-
方法一:
-
如上题目分析时所提到的方法,但过于复杂,不予推荐
-
-
方法二:
-
1、复制结点:首先复制链表的每一个结点,并将其连接到原链表对应结点的后面,如图所示。
-
2、复制随机指针:在同时含有原结点和复制结点的链表上,设置复制结点的随机指针的指向。
-
3、拆分链表:将链表的奇数位置的结点连接行成一个链表即为原链表,再把偶数位置的结点连接起来即为新复制的链表
-
-
-
图解法二
-
代码实现
RandomListNode* Clone(RandomListNode* pHead)
{
//当链表为NULL时,直接返回空
if (pHead == NULL)
{
return NULL;
}
RandomListNode*pNode = pHead;
RandomListNode*pClone = NULL;
//链表的连接
while (pNode != NULL)
{
pClone = (RandomListNode*)malloc(sizeof(RandomListNode));
pClone->label = pNode->label;
pClone->next = pNode->next;
pClone->random = NULL;
pNode->next = pClone;
pNode = pClone->next;
}
//随机指向的复制
pNode = pHead;
while (pNode != NULL)
{
//若random指向NULL,则按照初始化所赋值NULL执行,且pClone移向下一个复制结点
pClone = pNode->next;//不能放在if内部
if (pNode->random != NULL)
{
pClone->random = pNode->random->next;
pNode = pClone->next;
}
}
//拆分链表
pNode = pHead;
RandomListNode*pCloneHead = NULL;
if (pNode != NULL)
{
pCloneHead = pNode->next;
pClone = pNode->next;
pNode->next = pClone->next;
pNode = pNode->next;
}
while (pNode != NULL)
{
//注意此处拆分顺序,否则会造成数据丢失
pClone->next = pNode->next;
pClone = pClone->next;
pNode->next = pClone->next;
pNode = pNode->next;
}
return pCloneHead;
}