面试题35:复杂链表的复制
题目:请实现函数ComplexListNode* Clone(ComplexListNode* pHead),复制一个复杂链表。在复杂链表中,每个结点除了有一个pNext指针指向下一个结点之外,还有一个pSibling指向链表中的任意结点或者NULL。
复杂链表的结构体定义:
struct ListNode
{
int Value;
ListNode *Next, *Sibling;
ListNode(int value):Value(value),Next(nullptr),Sibling(nullptr){};
};
思路一:直接将整个链表进行复制,分两步进行。首先复制每个链表的下一个结点,然后复制每个结点的sibling,但是由于每个结点的sibling又需要从头开始遍历,因此每个结点需要O(n)步完成,那么n个结点的时间复杂度为O(n2)。代码如下:
class solution
{
public:
ListNode* clone(ListNode* Head)
{
ListNode* result = new ListNode(0);
ListNode* newNode = result;
ListNode* Node = Head;
while (Node)
{
ListNode* newItem = new ListNode(Node->Value);
newNode->Next = newItem;
Node = Node->Next;
newNode = newItem;
}
ListNode* newHead = result->Next;
delete result;
Node = Head;
newNode = newHead;
while (Node)
{
if (Node->Sibling)
{
ListNode* tempNode = newHead;
while (tempNode)
{
if (tempNode->Value == Node->Sibling->Value)
{
newNode->Sibling = tempNode;
break;
}
tempNode = tempNode->Next;
}
}
Node = Node->Next;
newNode = newNode->Next;
}
return newHead;
}
};
思路二:用空间换时间。将每个结点与复制的结点进行配对,放入hash表中,这样就可以通过旧结点的sibling快速找到新结点的sibling,时间复杂度为O(1),但是需要消耗O(n)的空间。代码如下:
class solution
{
public:
typedef map<ListNode*, ListNode*> Map;
ListNode* cloneNode(ListNode* Head, Map& hashNode)
{
ListNode* newHead = new ListNode(0);
ListNode* Node = Head;
ListNode* item = newHead;
while (Node)
{
ListNode* newNode = new ListNode(Node->Value);
item->Next = newNode;
hashNode.insert(make_pair(Node, newNode));
Node = Node->Next;
item = newNode;
}
ListNode* returnHead = newHead->Next;
delete newHead;
return returnHead;
}
void connectSibling(ListNode* Head, ListNode* newHead, Map& hashNode)
{
ListNode* Node = Head;
ListNode* newNode = newHead;
while (Node)
{
newNode->Sibling = hashNode[Node->Sibling];
Node = Node->Next;
newNode = newNode->Next;
}
}
ListNode* clone(ListNode* Head)
{
Map hashNode;
ListNode* newHead = cloneNode(Head, hashNode);
connectSibling(Head, newHead, hashNode);
return newHead;
}
};
思路三:在不使用辅助空间的情况下实现O(n)的时间效率。分为三步,首先在原链表中复制每个结点,并将该结点插入到原结点后面;然后根据原结点的sibling链接新结点的sibling(也就是说如果结点N的sibling是S,那么结点N1的sibling就是S1);之后将新链表和旧链表断开,得到复制的复杂链表。代码如下:
class solution
{
public:
void cloneNode(ListNode* Head)
{
ListNode* Node = Head;
while (Node)
{
ListNode* copyNode = new ListNode(Node->Value);
copyNode->Next = Node->Next;
Node->Next = copyNode;
Node = copyNode->Next;
}
}
void cloneSibling(ListNode* Head)
{
ListNode* Node = Head;
while (Node)
{
if (Node->Sibling)
Node->Next->Sibling = Node->Sibling->Next;
Node = Node->Next->Next;
}
}
ListNode* clone(ListNode* Head)
{
cloneNode(Head);
cloneSibling(Head);
ListNode* Node = Head;
ListNode* newHead = Head->Next;
ListNode* newNode = newHead;
while (Node)
{
Node->Next = newNode->Next;
if (Node->Next == nullptr)
break;
newNode->Next = Node->Next->Next;
Node = Node->Next;
newNode = newNode->Next;
}
return newHead;
}
};
测试样例如下:
测试代码如下:
int main()
{
ListNode* Node1 = new ListNode(1);
ListNode* Node2 = new ListNode(2);
ListNode* Node3 = new ListNode(3);
ListNode* Node4 = new ListNode(4);
ListNode* Node5 = new ListNode(5);
Node1->Next = Node2;
Node2->Next = Node3;
Node3->Next = Node4;
Node4->Next = Node5;
Node1->Sibling = Node3;
Node2->Sibling = Node5;
Node4->Sibling = Node2;
solution result;
ListNode* newHead = result.clone(Node1);
while (newHead != nullptr)
{
if (newHead->Sibling != nullptr)
printf("结点%d的Sibling为结点%d\n", newHead->Value, newHead->Sibling->Value);
newHead = newHead->Next;
}
return 0;
}
测试结果如下: