【剑指offer】26 - 复杂链表的复制

题目描述

题目:复杂链表中每个节点除了有一个 next 指针指向下一个节点外,还有一个 random 指针指向任意节点或者 NULL ,实现 ComplexListNode * Clone(RandomListNode* pHead) 复制一个复杂链表,返回结果为复制后复杂链表的头结点

节点的定义如下

struct RandomListNode {
    int val;
    struct ComplexListNode *next, *random;

    ComplexListNode(int x) 
        :val(x), next(NULL), random(NULL) 
    {}
};
解题思路

举例:我们首先以一个5个节点的复杂链表为例,分析它的结构,如下图所示:
复杂链表

分析

  • 首先,对于一般链表的复制,我们通常的思路是遍历整个链表,每拿到一个节点将其复制,然后通过 next 指针将其连接起来。
  • 此处的复杂链表不同于我们一般的链表,它的特殊之处在于 它的 random 指针,我们在复制的过程中不但要将 next 节点链接起来,而且要将 random 节点链接起来。这里,显然我们通常的遍历复制就不能达到这样的效果了,因为 random 的指向可能是当前节点前面已经遍历过的节点,这种情况是可以复制的,因为 random 节点已经复制存在;random 的指向还有可能是当前节点后面的我们还没有复制过的节点,此时我们如何进行链接呢?总之,通常的复制思路会带来一系列麻烦的操作,显然这种边复制边链接的思路是行不同的
  • 第二种思路:我们可以将复制过程分为两步,第一步先按照我们通常的思路遍历整个链表,复制每一个节点并用 next 指针将其链接起来;第二步遍历链表,对每一个节点设置 random 指针,因为 rondom 的指向是无规律的,每次找当前节点的 random 指针都要将链表从头进行遍历。这种方法是可以实现的,原链表当前节点的 random 指向距离头结点的距离是 x ,那么复制链表也要从头走 x 不才能定位到random 节点的位置,并且用 random 指针将其进行连接
  • 此时,我们分析第二种思路的时间复杂度:因为每次定位 random 节点都要从头节点开始经过 O(n)步才能找到,因此这种方法的总的时间复杂度为O(n2)
  • 第三种思路:上述第二种思路,我们主要的功夫花费在了rangdom 节点的定位上面,要想降低时间复杂度,我们需要从 random 指针的定位这一方面着手考虑。既然原链表节点和复制链表节点是由关联的,而且我们要做查找方面的优化,那我们可以考虑一种基于数组的数据结构——哈希表,在第一步复制节点并且以 next 指针相连接的同时将(A,A’)的配对信息放在哈希表中,第二步进行 random 指针的连接时,我们可以根据哈希表中的配对信息直接找到 random 节点,因为哈希表,我们的查找的时间复杂度就降低为O(1),但是 n 个节点我们需要一个O(n)的哈希表,这种思路是牺牲了空间来换取时间
  • 第四种思路:我们在上一步中已经将时间复杂度做了优化,进一步的优化我们需要考虑的是如何不借助辅助空间来实现时间复杂度为O(n)的效率。哈希表给我们的启发是:我们可以将原节点和复制节点放在一起,这样知道原节点的 random 节点的位置 就可以很快的知道 复制节点 random 节点的位置。
  • 如此,我们就可以这样解决:每复制一个节点,就将其插入到原节点的后面,这样我们复制节点的 random 节点就在原节点的下一个节点处,此时我们就可以用random 指针进行连接了。连接完成后,我们只需要将原链表与复制链表进行分离即可
代码实现

此处我们实现的是最后一种思路——时间复杂度为O(n),空间复杂度为O(1):

  • 第一步:复制原链表的任意节点并将其插入到原链表节点的后面,如下图所示:
    复杂链表复制step1
void CloneNodes(ComplexListNode* pHead)
{
    ComplexListNode* cur = pHead;
    while (cur != NULL)
    {
        //1.复制当前节点
        ComplexListNode* pClone = cur;
        pClone->next = cur->next;
        //2.复制节点插入到当前节点的后面
        cur->next = pClone;
        cur = pClone->next;
    }
}
  • 第二步:将复制节点通过 random 指针连接起来,若A节点的random指针指向C,则复制节点A’的random指针指向C’,设置好random指针的链表如下图所示:
    复杂链表复制step2
void ConnectRandomNodes(ComplexListNode* pHead)
{
    //若A节点的random指针指向C,则复制节点A'的random指针指向C’
    ComplexListNode* cur = pHead;
    while (cur != NULL)
    {
        ComplexListNode* pClone = cur->next;
        if (cur->random != NULL)
        {
            pClone->random = cur->random->next;
        }
        cur = pClone->next;
    }
}
  • 第三步:将和并后的链表进行拆分,奇数位置为原链表,偶数位置为复制链表,将当前节点的 next 指针与复制节点的 next 指针连接;将复制节点的 next 指针与当前节点的下一个节点进行连接,两条链表拆分后如下图所示:
    复杂链表复制step3
ComplexListNode* ReconnectNodes(ComplexListNode* pHead)
{
    ComplexListNode* cur = pHead;
    ComplexListNode* pNew = NULL;  //复制链表的头结点
    ComplexListNode* pClone = NULL;

    pNew = cur->next;
    while (cur->next!= NULL)
    {
        pClone = cur->next;
        cur->next = pClone->next;
        cur = pClone;
    }
    return pNew;
}
  • 完整实现代码如下:
#include <iostream>
using namespace std;

struct ComplexListNode {
    int val;
    struct ComplexListNode *next, *random;

    ComplexListNode(int x)
        :val(x), next(NULL), random(NULL)
    {}
};

void CloneNodes(ComplexListNode* pHead)
{
    ComplexListNode* cur = pHead;
    while (cur != NULL)
    {
        //1.复制当前节点
        ComplexListNode* pClone = cur;
        pClone->next = cur->next;
        //2.复制节点插入到当前节点的后面
        cur->next = pClone;
        cur = pClone->next;
    }
}

void ConnectRandomNodes(ComplexListNode* pHead)
{
    //若A节点的random指针指向C,则复制节点A'的random指针指向C’
    ComplexListNode* cur = pHead;
    while (cur != NULL)
    {
        ComplexListNode* pClone = cur->next;
        if (cur->random != NULL)
        {
            pClone->random = cur->random->next;
        }
        cur = pClone->next;
    }
}

ComplexListNode* ReconnectNodes(ComplexListNode* pHead)
{
    ComplexListNode* cur = pHead;
    ComplexListNode* pNew = NULL;  //复制链表的头结点
    ComplexListNode* pClone = NULL;

    pNew = cur->next;
    while (cur->next!= NULL)
    {
        pClone = cur->next;
        cur->next = pClone->next;
        cur = pClone;
    }
    return pNew;
}

ComplexListNode* Clone(ComplexListNode* pHead)
{
    CloneNodes(pHead);
    ConnectRandomNodes(pHead);
    return ReconnectNodes(pHead);
}

void PrintComplexList(ComplexListNode* pHead)
{
    ComplexListNode* cur = pHead;
    while (cur)
    {
        printf("[%d]:%d->", cur->val, cur->random->val);
        cur = cur->next;
    }
    printf("\n");
}

//测试代码:
int main()
{
    ComplexListNode* node1 = new ComplexListNode(1);
    ComplexListNode* node2 = new ComplexListNode(2);
    ComplexListNode* node3 = new ComplexListNode(3);
    ComplexListNode* node4 = new ComplexListNode(4);
    ComplexListNode* node5 = new ComplexListNode(5);
    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = NULL;

    node1->random = node3;
    node2->random = node5;
    node4->random = node2;

    ComplexListNode* copy = Clone(node1);
    cout << "原链表:" << endl;
    PrintComplexList(node1);
    cout << "复制链表:" << endl;
    PrintComplexList(copy);
    return 0;
}
  • 运行结果如下:
    复制结果

猜你喜欢

转载自blog.csdn.net/Aurora_pole/article/details/81352243