剑指offer之复杂链表的复制

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是22的m_pNext是33的m_pNext是44的m_pNext是55的m_pNext是nullptr

1的m_pSibling是32的m_pSibling是53的m_pSibling是nullptr4的m_pSibling是25的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_pSiblingN’是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
    ,第三步是拆分链表,每个函数都不是很复杂。只是比较难想到,很巧妙。

猜你喜欢

转载自blog.csdn.net/zqw_yaomin/article/details/81808283