复杂链表的复制
1、题目
请实现函数ComplexListNode* Clone(ComplexListNode* pHead),复制一个复杂链表。在复杂链表中,每个节点除了有一个m_pNext指针指向下一个节点,还有一个m_pSibling指针指向链表中的任意节点或者nullptr。节点的C++定义如下:
struct ComplexListNode
{
int m_nValue;
ComplexListNode* m_pNext;
ComplexListNode* m_pSibling;
};
输入参数:原始链表的头指针pHead
输出结果:复制链表的头指针pClonedHead
2、解题
这道题的关键在于拆分复杂问题让其简单化,复制一个复杂链表共有三种方法。
法一:
- 步骤
- 复制链表上的每个节点,并链接起来
- 设置每个节点的m_pSibling指针,所要定位的位置从原始链表的头节点开始找
- 时间复杂度:O(n^2)
法二:
- 步骤
- 复制链表上的每个节点,并链接起来。同时将m_pSibling指针的配对信息放在一个哈希表中
- 设置每个节点的m_pSibling指针,从哈希表中以O(1)时间找到即可
- 时间复杂度:O(n)
- 空间复杂度:O(n)
法三:
- 步骤
- 复制链表上的每个节点,将每个克隆节点放在原始节点的后面。
- 设置克隆节点的m_pSibling指针
- 将这个长链表拆分成两个链表,一个是原始链表,一个就是复制链表
- 在一个总的函数中调用上述的三个步骤(函数),最后返回复制链表的头指针pClonedHead
- 时间复杂度:O(n)
- 空间复杂度:不需要额外的空间
3、代码
这里采用第三种方法
//在原始节点后面克隆一个新节点
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 = nullptr;
//原始节点向前移动
pNode->m_pNext = pCloned;
pNode = pCloned->m_pNext;
}
}
//给克隆处的新节点的m_pSibling指针赋值
void ConnectSiblingNodes(ComplexListNode* pHead) {
ComplexListNode* pNode = pHead;
while (pNode != nullptr) {
ComplexListNode* pCloned = pNode->m_pNext;
//若原始节点的>m_pSibling指针为空,直接跳过即可
if (pNode->m_pSibling != nullptr) {
pCloned->m_pSibling = pNode->m_pSibling->m_pNext;
}
pNode = pCloned->m_pNext;
}
}
//从一个链表中分离出两个链表
ComplexListNode* ReconnectNodes(ComplexListNode* pHead) {
ComplexListNode* pNode = pHead;
ComplexListNode* pClonedHead = nullptr;
ComplexListNode* pClonedNode = nullptr;
//先找到头节点再说
if (pNode != nullptr) {
pClonedHead = pClonedNode = pNode->m_pNext;
//头节点赋值后,原始节点记得向前移动
pNode->m_pNext = pClonedNode->m_pNext;
pNode = pNode->m_pNext;
}
while (pNode != nullptr) {
//只有下一个原始节点存在,克隆节点才能继续前进
pClonedNode->m_pNext = pNode->m_pNext;
pClonedNode = pClonedNode->m_pNext;
//而后原始节点继续前进
pNode->m_pNext = pClonedNode->m_pNext;
pNode = pNode->m_pNext;
}
return pClonedHead;
}
ComplexListNode* Clone(ComplexListNode* pHead) {
CloneNodes(pHead);
ConnectSiblingNodes(pHead);
return ReconnectNodes(pHead);
}
4、注意点
- 在给克隆节点的m_pSibling指针赋值时,若原始节点的>m_pSibling指针为空,记得直接跳过即可
- 从一个链表中分离出两个链表的时候,需要先给复制链表的头指针赋值,再进行分离操作
- 分离时的步骤为:(此时pNode在pClonedNode之前)
- 先判空pNode,只有下一个原始节点pNode存在时,克隆节点pClonedNode才能继续前进
- 而后原始节点pNode继续前进,始终保持在pClonedNode的前面