题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)。
要求你编写函数复制这个复杂链表
复杂链表如下图所示
最简单暴力的方法:
很明显,链表分成两个部分,一个是下一个节点组成的链表,另一个特殊指针的链接。
那么我们的复制操作可以分成两个部分
代码如下
class Solution
{
public:
/// 找到newHead指向的新链表中与原来链表oldHead的randNode节点对应的那个节点
///
/// 复制的链表newHead与原链表oldHead存在一一对应的关系
/// 因此使用两个指针(一个指向原来链表一个指向新链表)同步移动
/// 即可找到newHead指向的新链表中与原来链表oldHead的randNode节点对应的那个节点
RandomListNode* FindInNew(RandomListNode *oldHead, RandomListNode *newHead, RandomListNode *randNode)
{
RandomListNode *currNode = oldHead;
RandomListNode *newNode = newHead;
while(currNode != NULL && newNode != NULL)
{
if(randNode == currNode)
{
return newNode;
}
currNode = currNode->next;
newNode = newNode->next;
}
return NULL;
}
/// 复制操作
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead == NULL)
{
return NULL;
}
RandomListNode *currNode = pHead;
RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL;
/// 首先复制原链表的普通指针域, 一次遍历即可完成
while(currNode != NULL)
{
newNode = new RandomListNode(currNode->label);
currNode = currNode->next;
if(preNode == NULL)
{
newHead = newNode;
}
else
{
preNode->next = newNode;
}
preNode = newNode;
}
// 接着复制随机指针域, 需要两次遍历
currNode = pHead;
newNode = newHead;
while(currNode != NULL && newNode != NULL)
{
RandomListNode *randNode = currNode->random; /// 随机指针域randNode
RandomListNode *newRandNode = FindInNew(pHead, newHead, randNode); /// 查找到新链表中与randNode对应的指针域
newNode->random = newRandNode;
/// 链表同步移动
currNode = currNode->next;
newNode = newNode->next;
}
return newHead;
}
};
其实分析一下子就知道我们的暴力解法的性能缺陷在哪?
我们每次复制随机指针的时候,找到newHead指向的新链表中与原来链表oldHead的randNode节点对应的那个节点时,需要同步遍历两个链表,这个过程是$O(n)$的
有没有什么方法把这个动作的时间复杂度降低到$O(1)$呢?
由于新旧链表中的节点是一一对应的,那么我们用空间换时间的方法,用一个map或者hashtable来存储这个映射关系
class Solution
{
public:
/// 复制操作
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead == NULL)
{
return NULL;
}
map<RandomListNode *, RandomListNode *> nodeMap;
RandomListNode *currNode = pHead;
RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL;
/// 首先复制原链表的普通指针域, 一次遍历即可完成
while(currNode != NULL)
{
newNode = new RandomListNode(currNode->label);
nodeMap[currNode] = newNode;
currNode = currNode->next;
if(preNode == NULL)
{
newHead = newNode;
}
else
{
preNode->next = newNode;
}
preNode = newNode;
}
// 接着复制随机指针域, 需要两次遍历
currNode = pHead;
newNode = newHead;
while(currNode != NULL && newNode != NULL)
{
RandomListNode *randNode = currNode->random; /// 随机指针域randNode
RandomListNode *newRandNode = nodeMap[randNode];
newNode->random = newRandNode;
/// 链表同步移动
currNode = currNode->next;
newNode = newNode->next;
}
return newHead;
}
};
上面那种方法,我们用map来存储新旧链表中结点的对应关系,对于右N个节点的链表,我么就用了一个大小为$O(N)$的map的空间消耗将时间复杂度从$O(N^2)$降到了$O(N)$ 那么有没有什么方法,能够将空间复杂度也降下来,在使用不用辅助空间的情况下实现$O(N)$的时间效率
其实我们需要的就只是能够建立新节点与原节点之前的对应关系就可以了, 链表中的顺序非随机访问方式,能够很简单的通过接单的nex指针t域查找下一个节点,那么我们将新节点直接插入到原结点的后面,这样可以很方便的通过原来节点找到新节点,
因此我们算法的流程如下:
class Solution
{
public:
/// 复制操作
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead == NULL)
{
return NULL;
}
RandomListNode *currNode = pHead;
RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL;
/// 首先复制原链表的普通指针域, 一次遍历即可完成
/// 将新的节点链接杂原来节点的末尾
while(currNode != NULL)
{
if((newNode = new RandomListNode(currNode->label)) == NULL)
{
perror("new error : ");
exit(-1);
}
/// 将新的节点newNode连接在currNode的后面
newNode->next = currNode->next;
currNode->next = newNode;
/// 指向指向下一个节点
currNode = newNode->next;
}
/// 接着复制随机指针域
/// 原来节点的下一个位置就是其对应的新节点
currNode = pHead;
newNode = pHead->next;
while(currNode != NULL)
{
RandomListNode *randNode = currNode->random; /// 随机指针域randNode
RandomListNode *newNode = currNode->next;
if(randNode != NULL)
{
newNode->random = randNode->next;
}
else
{
newNode->random = NULL;
}
/// 链表同步移动
currNode = newNode->next;
}
/// 将链接在一起的新旧两个链表拆分开
/// 脱链,更新各链表的next指针
currNode = pHead;
newNode = newHead = pHead->next;
while(currNode != NULL)
{
/// curr new new->next
currNode->next = newNode->next;
debug <<currNode->label <<", " <<newNode->label <<endl;
if(newNode->next != NULL)
{
newNode->next = newNode->next->next;
}
else
{
newNode->next = NULL;
}
currNode = currNode->next;
newNode = newNode->next;
}
return newHead;
}
};
aa1565d85a362288728f966d99a87c21309698ad