[LeetCode] 138.复制带随机指针的链表


image.png

本文带大家来做一道LeetCode上链表题,这道题在剑指offer这本书中同样出现过,非常值得研究的一道题
image.pngimage.png

在LeetCode是138题 传送门

有很多朋友看到这么密密麻麻的题目有很头疼,觉得自己看不懂,其实细心看一下也就还好,本身表达的已经很清楚

这道题的是意思就是让你

给你一个链表,每个节点增加了一个元素, 之前不是只有val , next 么,现在加了一个random, random指向链表中的任意一个位置,或者为NULL
让你复制一个链表出来, 状态一样,如果没有这个random的话,复制一个链表不是简简单单,关键是这个random要指向新链表的相对位置

我们来分析一下思路

思路一 : 暴力算法O(N ^ 2)

相信大家肯定想的暴力算法,先复制一个新链表出来,然后去看看原来的链表节点的random指向的是第几个,
然后让新链表的也指向新链表的第几个就可以了

第一步:拷贝链表

image.png

复制链表,新的头是copyHead, 然后处理最难处理的 random,

第二步:处理random

通过原链表找相对位置, 然后让复制链表也指向对应的位置

我们来分析一下
image.png

在这里定义的变量比较多, srcCur代表找的遍历节点,copyCur自然是复制链表的遍历节点
srcTmp , copyTmp 都是从头开始往后走遍历找的节点
pos代表相对位置,你可以当做下标来理解

完整代码
struct Node* copyRandomList(struct Node* head) 
{
    if(NULL == head)
        return NULL;
    //复制新链表
    struct Node* copyHead = NULL,*copyTail = NULL;
    struct Node* cur = head;
    while(cur)
    {
        //存储cur的下一个
        struct Node* next = cur->next;
        //malloc开辟新节点
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->val = cur->val;
        //这里一定要置空
        newNode->next = NULL;
        //尾插
        if(copyHead == NULL)
        {
            //第一次尾插需要赋值
            copyHead = copyTail = newNode;
        }
        else
        {
            copyTail->next = newNode;
            //后移
            copyTail = copyTail->next;
        }
        //迭代
        cur = next;
    }
    //处理pos,找相对位置
    struct Node* srcCur = head;
    struct Node* copyCur = copyHead;
    while(srcCur)
    {
        //如果为空,那么copyCur->random = NULL;
        if(srcCur->random == NULL)
        {
            copyCur->random = NULL;
        }
        else //2.如果不为空,就去找相对位置
        {
            //用来查找的头结点,每次都从头开始
            struct Node* copyTmp = copyHead;
            struct Node* srcTmp = head;
            int pos = 0;
            //原链表找
            while(srcTmp)
            {
                if(srcTmp == srcCur->random)
                {
                    break;
                }
                pos++;
                srcTmp = srcTmp->next;
            }
            //复制链表找,
            while(pos--)
            {
                copyTmp = copyTmp->next;
            }
            //赋值
            copyCur->random = copyTmp;
        }
        srcCur = srcCur->next;
        copyCur = copyCur->next;
    }

    return copyHead;
}

image.png

可以看到,虽然复杂度比较高,但是还是能跑过的,时间复杂度为O(N^2) , 但是我们总是想着去找更优的算法,可不可以降低到O(N)呢,当然是可以的,我们来看思路二

思路二: 接到相邻位置

这个思路很难想到,听我细细道来,先给 大家放一个图

image.png
大家可以看一下这个图,我们把复制的节点都拼接到原来节点的后面,这个还是比较容易的,大家能不能看出复制节点的random如何找到正确正确的位置呢? ???

好, 这个思路大约有三步,

  1. 复制节点,拼接到后面
  2. 处理复制节点的random
  3. 把节点拆出来, 然后组成新链表,恢复原链表
第一步:复制节点,拼接到后面

这个还是比较相对比较简答的,我们分析一下

image.png

第二步:处理复制节点的random

这道题的难点就在于如何处理复制节点的random, 我们来分析一下

image.png

第三步:把节点拆出来, 然后组成新链表,.恢复原链表

最难的random问题解决了,我们就来把这个链表拆出来, 组成复制链表

其实就像删除节点一样,只不过是组成新节点,还是比较简单的
image.png

这里不重新恢复原链表也能过,但是最好还是恢复一下

Code
struct Node* copyRandomList(struct Node* head) 
{
    if(NULL == head)
        return NULL;
    //复制节点,拼接到后面
    struct Node* cur = head;
    struct Node* copyHead = NULL, *copyTail = NULL;

    while(cur)
    {
        //开辟新节点
        struct Node* copyNode = (struct Node*)malloc(sizeof(struct Node));
        //调整新节点指向
        copyNode->val = cur->val;
        copyNode->next = cur->next;
        //拼接
        cur->next = copyNode;
        //迭代
        cur = copyNode->next;
    }

    //处理复制random问题
    //重置cur
    cur = head;
    while(cur)
    {
        struct Node* copyNode = cur->next;
        if(cur->random == NULL)
        {
            copyNode->random = NULL;
        }
        else
        {
            copyNode->random = cur->random->next;
        }
        //迭代
        cur = copyNode->next;
    }
    //把节点拆出来, 然后组成新链表,.恢复原链表
   
    cur = head;
    while(cur)
    {
        struct Node* copyNode = cur->next;
        if(copyHead == NULL)
        {
            copyHead = copyTail = copyNode;
        }
        else
        {
            copyTail->next = copyNode;
            copyTail = copyTail->next;
        }
        cur->next = copyNode->next;
        //迭代cur
        cur = copyNode->next;
    }
    return copyHead;
}

总结

这道题可以练习大家对于链表基础操作的掌握,很有针对性
这道题到这里就结束了,希望大家可以画图分析,自己实现,感谢收看,
image.png

  • 12
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值