1.问题描述
题目:请实现函数ComplexListNode*Clone(ComplexListNode*pHead),复制一个复杂链表。在复杂链表中,每个结点除了有一个m_pNext指针指向下一个结点外,还有一个m_pSibling指向链表中的任意结点或者nullptr。
节点的c++定义如下:
struct ComplexListNode
{
int m_nValue;
ComplexListNode* m_pNext;
ComplexListNode* m_pSibling;
};
2.分析
例如:
// -----------------
// \|/ |
// 1-------2-------3-------4-------5
// | | /|\ /|\
// --------+-------- |
// -------------------------
其中1的m_pNext是2,2的m_pNext是3,3的m_pNext是4,4的m_pNext是5,5的m_pNext是nullptr;
1的m_pSibling是3,2的m_pSibling是5,3的m_pSibling是nullptr,4的m_pSibling是2,5的m_pSibling是nullptr。
方法一
第一步,复制原始链表上的每一个节点,并用m_pNext连接起来;
第二步,设置每个节点的m_pSibling指针。
第一步好说,挺简单的。但是第二步就不容易了,假设N节点的m_pSibling指向的节点M,而M节点可以在N的前面,也可以在N的后面,设置可以是N本身。所以,我们需要从第一个节点开始遍历,计数需要X步到达M节点,X步也是N’节点到M’节点的步数。
所以,定位每个节点的m_pSibling都需要链表头开始O(n)步才能找到,一次对于n个节点,则总的时间复杂度是一样的。
方法二:
第一个和上面一样,复制原始链表上的每一个节点,并用m_pNext连接起来,只是使用哈希表保存原始节点,和复制节点;
第二步,设置每个节点的m_pSibling指针,因为上面有了哈希表,可以在O(1)找到N节点的m_pSibling对应的N’节点的m_pSibling节点,只需要从链表头开始,每个节点设置m_pSibling。时间复杂度是O(n),但是空间复杂度是O(n),因为这里使用了一个哈希表。
方法三
第一步:复制原始链表上的每一个节点,并且把复制节点添加到原始节点后面;
第二步:设置链表的m_pSibling,因为N’的m_pSibling’对应N的m_pSibling,N’是N的m_pNext,m_pSibling’是m_pSibling的m_pNext。
所以这样也可以很快就设置好了m_pSibling;
第三步:拆分链表,把链表分成两个链表。
3.源代码:
方法二:
ComplexListNode* Clone(ComplexListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
//建立一个头指针
ComplexListNode* pClonedHead = new ComplexListNode();
pClonedHead->m_nValue = pHead->m_nValue;
pClonedHead->m_pNext = nullptr;
pClonedHead->m_pSibling = nullptr;
map<ComplexListNode* ,ComplexListNode*> ListNodeMap;
ListNodeMap.insert(std::pair<ComplexListNode* ,ComplexListNode*>(pHead,pClonedHead));
ComplexListNode* pNode = pHead->m_pNext;
ComplexListNode* pClonedNode = pClonedHead;
while(pNode !=nullptr)
{
//新建一个节点
ComplexListNode* pClonedNextNode = new ComplexListNode();
pClonedNextNode->m_nValue = pNode->m_nValue;
pClonedNextNode->m_pNext = nullptr;
pClonedNextNode->m_pSibling = nullptr;
//把新建的节点添加到pClonedNode中
pClonedNode->m_pNext = pClonedNextNode;
pClonedNode = pClonedNode->m_pNext;
//同时添加到map中
ListNodeMap.insert(std::pair<ComplexListNode* ,ComplexListNode*>(pNode,pClonedNode));
//pNode指向下一个
pNode = pNode->m_pNext;
}
pNode = pHead;
pClonedNode = pClonedHead;
while(pNode != nullptr)
{
ComplexListNode* pSipling = pNode->m_pSibling;
if(pSipling !=nullptr)
{
ComplexListNode* pCloneSipling = ListNodeMap.find(pSipling)->second;
pClonedNode->m_pSibling = pCloneSipling;
}
pNode = pNode->m_pNext;
pClonedNode = pClonedNode->m_pNext;
}
return pClonedHead;
}
方法三:
void CloneNodes(ComplexListNode* pHead)
{
ComplexListNode* pNode = pHead;
while(pNode !=nullptr)
{
ComplexListNode* pCloned = new ComplexListNode();
pCloned->m_nValue = pNode->m_nValue;
pCloned->m_pNext = pNode->m_pNext;
pCloned->m_pSibling = pNode->m_pSibling;
pNode->m_pNext = pCloned;
pNode = pCloned->m_pNext;
}
}
void ConnectSiblingNodes(ComplexListNode* pHead)
{
ComplexListNode* pNode = pHead;
while (pNode !=nullptr)
{
ComplexListNode* pSiblingNode = pNode->m_pSibling;
ComplexListNode* pCloned = pNode->m_pNext;
if(pSiblingNode != nullptr)
pCloned->m_pSibling = pSiblingNode->m_pNext;
pNode = pCloned->m_pNext;
}
}
ComplexListNode* ReconnectNodes(ComplexListNode* pHead)
{
ComplexListNode* pNode = pHead;
ComplexListNode* pClonedHead = nullptr;
if(pNode !=nullptr)
{
pClonedHead = pHead->m_pNext;
pNode->m_pNext = pClonedHead->m_pNext;
pNode = pNode->m_pNext;
}
ComplexListNode* pCloned = pClonedHead;
while(pNode !=nullptr)
{
pCloned->m_pNext = pNode->m_pNext;
pCloned = pCloned->m_pNext;
pNode->m_pNext = pCloned->m_pNext;
pNode = pNode->m_pNext;
}
return pClonedHead;
}
ComplexListNode* Clone(ComplexListNode* pHead)
{
CloneNodes(pHead);
ConnectSiblingNodes(pHead);
return ReconnectNodes(pHead);
}
4.结论
- 第一种方法容易想到,但是时间复杂度是O(n2),并且编写起来比较复杂;
- 第二种方法也比较简单,时间复杂度是O(n),但是空间复杂度也是O(n),但是这种方法比较容易编写,但是需要编程人员熟悉哈希表使用;
- 第三种方法,是把一个大问题分成3个小步骤,第一步复杂节点,第二步,设置m_pSibling
,第三步是拆分链表,每个函数都不是很复杂。只是比较难想到,很巧妙。