【算法】如果链表里有随机指针怎么拷贝?-【力扣-138】复制带有随机指针的链表-力扣经典题目讲解【带图】【保姆级别教程】

【算法】如果链表里有随机指针怎么拷贝?-【力扣-138】经典题目讲解【保姆级别教程】
先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力 看完之后别忘记关注我哦!️️️

题目:力扣OJ138-复制带随即指针的链表

本篇建议收藏后食用~

题目

在这里插入图片描述
在这里插入图片描述

分析

在这里插入图片描述

题目初步分析:
看完这道题,我们首先要知道的-什么是带有随机指针的链表。
当然,题目写的太“高级”,感觉很厉害的样子,其实没那么难理解
我们通过leetcode提供给我们的结构图,我们可以看到。
这个特殊的链表总体结构上是一个单链表,与单链表不同的是,这个链表的每一个结点里多了一个random指针,它指向的结点是不确定的有可能指向第一个结点,有可能指向第二个,有可能指向它自己,有可能指向NULL。这就给我们的拷贝制造了很多麻烦。
当然,这道题对于初学数据结构的伙伴来说,算是一道比较难的题目了,所以,看完这篇博客,相信我们可以很好地解决这个问题,并且我相信我们的能力,会有一个层次的提升。

一些关于学数据结构的提醒

我曾经在以前的博客里面讲过,学习编程,特别是学到指针,数据结构这些内容的时候。画图是非常重要的,这些题目,虽然我学习的时间并不是很久,但是我可以很负责任地说,学编程,不画图,一定学不好。伙伴们看看这道题,不画图根本不可能想得出来。

算法及实现

链表结构

struct Node {
    int val;
    struct Node *next;
    struct Node *random;
 };

解题思路

首先:想要解决这一道题,想要直接拷贝是肯定不行的,如果我们拷贝出来的空间不和原来的结点有联系的话,random是处理不了的。
所以,我们的新开辟出来的结点一定要和原来的结点有联系。因此
第一步:将新拷贝的结点链接到我们原来的结点的后面。并重新链接起来。做完这个步骤,原来三个结点的链表会变成六个结点,原来四个结点的链表会变成八个结点。
即如图所示
在这里插入图片描述

做完这一步,我们新拷贝的结点就和我们原来的结点建立了联系。

第二步: 处理random指针: 做完第一步之后,我们要敏锐地发现,拷贝出来的结点的random所指向的结点,应该是原来结点的random所指向的结点的后面那个结点。这样我们的random不就可以处理了吗。
我们再梳理一次上面那句话:新拷贝出来的结点的random指向原来拷贝出来的random所指向的结点的后面那个结点(当然,如果原来random指向的是NULL,那我们新random也直接指向NULL就可以了,这里记得判断一下
,否则容易造成对空指针解引用的问题,导致OJ过不了。)
这个就是整道题的精髓所在,处理random的方法。

做完第二步之后,我们就处理好了random指针的问题,最麻烦的一步解决了。

第三步:断开新链表和旧链表的联系
目前的状况是这样子的:(由于加上random的箭头,版面太乱了,图片里就不加上箭头了,用“已处理好”代替)
在这里插入图片描述
现在需要做的,就是断开二者联系,使它成为一个独立链表。
首先,我们需要三个指针,防止结点的丢失cur(用于遍历),copy(cur后面拷贝的结点),next(copy后面的那个结点,也就是原链表cur后面的那个结点)
这里只要注意一下拆指针的顺序即可,这一部分我们在其它的题也接触很多了。这里就不赘述了。

完整代码

struct Node* copyRandomList(struct Node* head) {
    if (head == NULL) {//防止传进来空链表
        return NULL;
    }
    struct Node* cur = head;
    //1.拷贝链表,放到原来结点的后面
    while (cur) {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->next = NULL;
        copy->val = cur->val;

        copy->next = cur->next;//先让新的指向后面
        cur->next = copy;//然后再让前面的指向新的
        //注意顺序

        //让指针动起来(迭代)
        cur = cur->next->next;
    }
    //2.处理random
    cur = head;//cur重新来过
    while (cur) {
        struct Node* copy = cur->next;
        //写链表一定要细心再细心
        //题目说random有可能为空,所以:
        if (cur->random != NULL) {//记得判断一下
            copy->random = cur->random->next;//关键
        }
        else {
            copy->random = NULL;
        }
        //迭代
        cur = cur->next->next;//因为中间间隔了一个拷贝结点
    }
    //3.拆
    //这里一定要画图分析注意一下
    //三个指针,cur copy next,当走到最后一步的时候,next已经是空了,这个时候copy=copy->next->next就不对了,应该直接置成空
    //此时我们发现,我们的函数是要返回新的头结点的,但是我们弄丢了,所以要保存
    cur = head;
    struct Node* copyhead = head->next;//先保存头
    while (cur) {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;

        cur->next = next;
        if (next != NULL) {//最后一步防止next已经为空了
            copy->next = next->next;
        }

        //迭代
        cur = next;
    }
    return copyhead;
}

在这里插入图片描述

尾声

以上就是这期博客所带来的-复制带有随机指针的链表的力扣经典题。这题对于初学者来说难度确实有点高。但是相信看到这里的伙伴,应该已经可以基本完成这道题了。这边建议下去以后我们再独立做一遍。如果你感觉这篇博客对你有帮助的话,别忘了点赞关注收藏转发哦!你们的支持是我必不可少的动力

posted @ 2021-12-18 15:50  背包Yu  阅读(4)  评论(0编辑  收藏  举报  来源