题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
解题思路
一开始只想着暴力复制,第一遍遍历先复制next结点,第二遍遍历再用查找那个结点值的方法返回random指向的那个结点。这种方法有两个问题:一个是时间复杂度是O(n^2),一个就是如果结点值不唯一则复制random的时候会出错。书上先是提供了hash保存原结点和复制结点的对应关系的方法(时间复杂度O(n),空间复杂度O(n)),然后是提供一种更巧的方法(时间复杂度O(n),空间复杂度O(1)),这里重点介绍一下最后一种方法,思想是分治法,分而治之。
第三种方法的第一步仍然是根据原始链表的每个节点N创建对应的N’。这一次,我们把N’链接在N的后面。如下图。
Code
- O(n^2)版本
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* searchRandomNode(RandomListNode* cloneHead, int label) {
RandomListNode *currentNode = cloneHead;
while(currentNode) {
if(currentNode->label == label) {
return currentNode;
}
currentNode = currentNode->next;
}
return nullptr;
}
RandomListNode* Clone(RandomListNode* pHead)
{
RandomListNode *cloneHead = nullptr, *currentNode = nullptr, *preNode = nullptr, *originHead = pHead;
while(pHead) {
preNode = currentNode;
RandomListNode* newNode = new RandomListNode(pHead->label);
currentNode = newNode;
if(cloneHead == nullptr) {
cloneHead = currentNode;
}
if(preNode != nullptr) {
preNode->next = currentNode;
}
pHead = pHead->next;
}
currentNode = cloneHead;
while(currentNode) {
if(originHead->random) {
currentNode->random = searchRandomNode(cloneHead, originHead->random->label);
}
currentNode = currentNode->next;
originHead = originHead->next;
}
return cloneHead;
}
};
- 书上的代码
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
void CloneNextNodes(RandomListNode* pHead) {
RandomListNode* pNode = pHead;
while(pNode) {
RandomListNode* pCloneNode = new RandomListNode(pNode->label);
pCloneNode->next = pNode->next;
pNode->next = pCloneNode;
pNode = pCloneNode->next;
}
}
void CloneRandomNodes(RandomListNode* pHead) {
RandomListNode* pNode = pHead;
while(pNode) {
RandomListNode* cloneNode = pNode->next;
if(pNode->random) {
cloneNode->random = pNode->random->next;
}
pNode = cloneNode->next;
}
}
RandomListNode* ReconnectNodes(RandomListNode* pHead) {
RandomListNode *pNode = pHead, *pCloneNode = nullptr, *pCloneHead = nullptr;
if(pNode) {
pCloneHead = pCloneNode= pNode->next;
pNode->next = pCloneNode->next;
pNode = pNode->next;
}
while(pNode) {
pCloneNode->next = pNode->next;
pCloneNode = pNode->next;
pNode->next = pCloneNode->next;
pNode = pNode->next;
}
return pCloneHead;
}
RandomListNode* Clone(RandomListNode* pHead)
{
CloneNextNodes(pHead);
CloneRandomNodes(pHead);
return ReconnectNodes(pHead);
}
};
- java
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead == null) return pHead;
RandomListNode pNode = pHead, cHead, pCopy;
while(pNode != null) {
pCopy = new RandomListNode(pNode.label);
pCopy.next = pNode.next;
pCopy.random = pNode.random;
pNode.next = pCopy;
pNode = pCopy.next;
}
pNode = pHead;
while(pNode != null) {
pCopy = pNode.next;
if(pCopy.random != null) {
pCopy.random = pCopy.random.next;
}
pNode = pCopy.next;
}
pNode = pHead;
cHead = pHead.next;
while(pNode != null) {
pCopy = pNode.next;
pNode.next = pCopy.next;
pNode = pNode.next;
if(pNode != null) pCopy.next = pNode.next;
}
return cHead;
}
}
总结
- 传入指针后是传地址,会改变原来的指针
- 因此传头节点进入时,new一个指针指向头节点并用这个指针进行遍历。