复制带随机指针的链表又名深度拷贝

138. 复制带随机指针的链表 - 力扣(Leetcode)

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

复制带随机指针的链表又名深度拷贝_第1张图片

复制普通单链表,简单保存尾插节点就可以

而复制带随机指针的链表并不容易。

我在这里有四种算法:

  • 暴力法

  • 循环链表。

  • 数组保存链表节点。

  • 将复制链表每个节点插入在原链表节点后面

1.暴力求解

  1. 先保存原节点random中的地址,然后从头遍历链表,并且做一个计数器每走一节点,计数器++,找到后,根据计数器的数据,从头移动在复制链表中移动,每移动一步就计数器自减,在计数器为0时,将复制节点的random保存该该地址,

struct Node* orList_cur=slist;
struct Node*copy=copy_head;
while(orList_cur)
{
    if(orList_cur->random==NULL)
    {
        copy->random==NULL;
    }
    else
    {
    struct Node* orList_cur_random=orList_cur->random;
    int coust=0;//计数器
    struct Node*move=Slist;
    while(move==orList_cur_random)
    {
        coust++;//
        move-move->next;
    }
    struct Node*move_copy=copy_head;//copy_head链接完毕后的复制链表头节点
    while(coust--)
    {
        move_copy=move_copy->next;//寻找正确的random位置;
    }
         copy->random=move_copy;
    }
    //迭代
    orList_cur=orList_cur->next;
    copy=copy
}

根据随机原链表指向链表的第几个元素,复制链表也指向链表的该位置。

这种方法得不偿失,需要的每一个节点都需要遍历原链表时间复杂度为O(N^2);

  • 2.循环链表。

我们将原链表的头尾连接记录头节点位置

复制带随机指针的链表又名深度拷贝_第2张图片

注意or_head节点不可动

先复制原链表的next与val

复制带随机指针的链表又名深度拷贝_第3张图片

注意copy_head节点不可动

也是计数法确定位置但此刻,开始出当前节点向后寻找

复制带随机指针的链表又名深度拷贝_第4张图片

or_move指针开始移动,每移动一步计数器+1,当or_move==or_cur

复制带随机指针的链表又名深度拷贝_第5张图片

当or_move到了or_cur->random时停止循环,

利用计数器值,让copy_move要走多少步,到copy_cur->random应该指向的位置

复制带随机指针的链表又名深度拷贝_第6张图片

在将copy_cur->random指向copy_move

复制带随机指针的链表又名深度拷贝_第7张图片

关键代码如下

struct Node*or_cur=or_head->next;
struct Node*copy_cur=copy_head->next;
while(or_cur!=slist)//当cur==slist时停止循环,做最后处理
{

    if(or_cur->random==NULL)
    {
        copy_cur->random==NULL;
    }
    else  
    {
        int coust=0;
        struct Node*or_move=or_cur;
        //记录走了多少步
        while(or_move!=or_cur->random)
        {
            or_move=or_move->next;
            coust++;
        }
       struct Node* copy_move=copy_cur->next;
       while(coust--)
       {
           copy_move=copy_move->next;
       }
    copy_cur->random=copy_move;
    //迭代
    orList_cur=orList_cur->next;
    copy=copy
    }
}
    if(or_head->random==NULL)
    {
        copy_head->random==NULL;
    }
    else if(or_head->random==or_head)
    {
        copy_head->random=copy_head;
    }
    else  
    {
        int coust=0;
        struct Node*or_move=or_head->random;
        //记录走了多少步
        while(or_move!=or_head->random)
        {
            or_move=or_move->next;
            coust++;
        }
       struct Node* copy_move=copy_head->next;
       while(coust--)
       {
           copy_move=copy_move->next;
       }
       copy_cur->random=copy_move;
     }

3.数组存储

将每个原节点存入到指针数组中

利用数组复制链表,也是利用计数器原理,所有大同小异。

4.交叉拷贝

将每个拷贝节点都链接在原链表后面,然后在根据结构关系,了解random指针。

首先拷贝原节点数据到copy节点

复制带随机指针的链表又名深度拷贝_第8张图片

然后再插入到原节点后

复制带随机指针的链表又名深度拷贝_第9张图片

循环反复完成所有copy节点的链接关系,构建新链表

复制带随机指针的链表又名深度拷贝_第10张图片

看图

复制带随机指针的链表又名深度拷贝_第11张图片

会发现5的random的next正好是copy-5的random该指向的位置。

所以我们发现,原节点的random的next,正好是copy节点random该指向的位置

复制带随机指针的链表又名深度拷贝_第12张图片

迭代前进。

复制带随机指针的链表又名深度拷贝_第13张图片

将所有的都链接完毕

断开链接,恢复原链表链接关系,要保存copy的头节点,使用双指针。

复制带随机指针的链表又名深度拷贝_第14张图片

最后返回copy_head;

时间复杂度O(N);

代码

struct Node* copyRandomList(struct Node* head) {
    struct Node*cur=head;

    //将复制节点插入再原节点后面。
    while(cur)
    {
       //创造节点
        struct Node*copy=(struct Node*)malloc(sizeof(struct Node));
        copy->val=cur->val;
        //链接cur copy next
        struct Node*next=cur->next;
        cur->next=copy;
        copy->next=next;
        //迭代
        cur=next;
    }
    //处理拷贝节点得random
    cur=head;
    while(cur)
    {
        struct Node*copy=cur->next;
        if(!cur->random)
        {
            copy->random=NULL;
        }
        else
        {
            //最关键得一句话
            copy->random=cur->random->next;
            //迭代            
        }
        cur=copy->next;
    }
    cur=head;
    //解开copy链表,返回copy链表;
    struct Node*copyhead=NULL;
    struct Node*copytail=NULL;
    while(cur)
    {
        struct Node*copy=cur->next;
        struct Node*next=copy->next;
        //copy尾插
        if(copyhead==NULL)
        {
            copyhead=copytail=copy;
        }
        else
        {
            copytail->next=copy;
            copytail=copy;
        }
        cur->next=next;
        cur=next;
    }
    return copyhead;
}

推荐第四种方法,时间复杂度大大减低,而且容易理解。

谢谢观看。

你可能感兴趣的:(链表,数据结构)