题目:Copy List with Random Pointer

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

题意:普通的链表中给定一个随机指针,该指针可以为空,也可以指向链表中任意节点。要求对该链表深拷贝。

思路:

与普通的链表拷贝不同的是,随机链表中的指针深拷贝时,是指向对应的拷贝的节点,他可能指向的节点还没有生成出来,因此,不能遍历一遍就拷贝出来。

因此,需要在生成出所有的节点之后,再去找到每个节点的随机指针指向的节点,但是,此时如何定位该节点在复制的链表的位置呢?

第一个思路是使用值来确定,使用map将每个节点的值和其节点指针映射到一起,这样就可以通过值来确定节点。

即,首先,按照正常的单链表的深拷贝来拷贝一个链表,同时,以每个节点的值为key,将每个节点加到map中;

然后,重新遍历一边链表,通过随机指针指向的节点的值,从map中找到对应的节点指针赋给随机指针就可以了。

/**
 *使用unordered_map来加快查找速度
 *时间复杂度O(n),空间复杂度O(n)
 **/
RandomListNode *copyRandomList(RandomListNode *head){
    if (!head)return nullptr;//链表为空
    unordered_map<int, RandomListNode*>hmp;
    RandomListNode* cpHead = new RandomListNode(head->label);//复制头结点
    RandomListNode* p = head->next,* q = cpHead;
    hmp.insert(make_pair(head->label, cpHead));//插入unordered_map中
    while (p){//循环复制链表的所有节点
        RandomListNode* rln = new RandomListNode(p->label);
        q->next = rln;
        hmp.insert(make_pair(p->label,rln));//并将当前的节点和复制节点都插入到unordered_map中
        p = p->next;
        q = rln;
    }
    p = head;
    q = cpHead;
    while (p){//遍历链表,找到random对应的复制节点,将它赋给复制链表的random
        if (p->random){
            auto it = hmp.find(p->random->label);
            q->random = (*it).second;
        }
        p = p->next;
        q = q->next;
    }
    return cpHead;
}

思路二:

上面的思路没有办法解决链表两个不同节点但是值相同的情况。

于是想到将复制的节点先不组成链表,而是将它插入到原链表的对应的原节点的的下一个位置;

这样再处理拷贝的节点的随机指针时,拷贝的节点的随机链表的指针只需要指向原节点的随机指针指向的节点的下一个节点就可以了;

最后在将拷贝的节点还原成链表就完成了深拷贝。

/**
 *A->B->C->D->E
 *A->a->B->b->C->c->D->d->E->e
 **/
RandomListNode *copyRandomList2(RandomListNode *head){
    if (!head)return nullptr;//链表为空
    RandomListNode* p = head;
    while (p){//遍历链表,复制每个节点,并将复制的节点放到对应节点的下一个位置
        RandomListNode* rln = new RandomListNode(p->label);
        rln->next = p->next;//放到下一个位置
        p->next = rln;
        p = rln->next;
    }
    p = head;
    RandomListNode* cpHead = p->next,* pre = nullptr;
    if (p->random){//头结点的random是否为空
        cpHead->random = p->random->next;//连接头节点的random
    }
    p->next = cpHead->next;//将链表还原
    pre = cpHead;
    p = p->next;
    while (p){//遍历链表,找到random对应的复制节点,将它赋给复制链表的random
        RandomListNode *cp = p->next;//当前节点的复制节点
        if (p->random){//连接random
            cp->random = p->random->next;
        }
        pre->next = cp;//将复制节点加入复制链表中
        p->next = cp->next;//还原链表
        pre = cp;
        p = p->next;
    }
    return cpHead;
}