链表经典OJ题——复制带随机指针的链表(C语言实现)

目录

题目链接与介绍

解析题意

具体操作步骤

步骤一:

具体图解:

具体代码: 

步骤二:

具体图解:

 具体代码:

步骤三:

具体图解:

具体代码:

 完整代码


题目链接与介绍

力扣

解析题意

1、简单的来说其实就是,现在有一个单链表,它不仅有next指针指向后边的结点。还有random指针指向一个随机的结点。 

2、现在要对其进行“深拷贝”,注意:这里是深拷贝,也就是说要复制一个一模一样的链表,与题目所给的链表一模一样,也就是需要重新创建新结点,然后进行遍历链接工作。

3、新链表与旧链表是不能有指向关系的,也就是他们是两个独立的链表,并且对应的结点中的val值还需相同。

当我们已经明白了题意之后,就可以对其进行相应步骤的操作了。

具体操作步骤

步骤一:

主要进行创建新结点copy(并给旧结点赋上对应新结点的值),在遍历新链表与旧链表的同时。并将新结点与旧结点进行链接,还需给对应新结点赋上旧结点的值。

具体图解:

具体代码:


    // 步骤一
    // 主要进行创建新结点copy(并给旧结点赋上对应新结点的值),并将新结点与旧结点进行链接

    // 给cur赋初值便于进行旧表结点的操作
    struct Node* cur=head;

    // 开始遍历
    while(cur)
    {
        // 定义一个next便于进行链接
        struct Node* next = cur->next;
        // 创建新结点copy,为新链表做基础
        struct Node* copy=(struct Node*)malloc(sizeof(struct Node));

        // 给新结点赋上对应旧结点的值
        copy->val=cur->val;

        // 链接操作

        // 旧结点链接新结点
        cur->next=copy;
        // 新结点链接旧结点
        copy->next=next;
        // 继续遍历
        cur=next;
    }

 

步骤二:

目的是为了解决新结点random的链接问题。

如果新结点对应旧结点的random为空那么它的random也是空。

若不为空,则刚好指向对应新结点random的next(原因是此时旧结点的next恰好指向的就是相对应的新结点!)。

具体图解:

 具体代码:


// 步骤二
// 目的是为了解决新结点random的链接问题

    // 重新赋值,重新遍历
    cur=head;

    // 遍历操作
    while(cur)
    {
        // 第一次进入循环是给copy赋初值
        // 接下老就是遍历新结点的意思了
        struct Node* copy=cur->next;

        // 如果对应旧结点的random!=NULL  
        // 然后根据旧结点的next刚好就是对应的新结点
        // 让新结点的random=旧结点的random的next
        if(cur->random!=NULL)
        {
            copy->random=cur->random->next;
        }
        // 如果cur->random是一个NULL
        // 那么毋容置疑copy->random一定是个NULL
        else
        {
            copy->random=NULL;
        }
        // cur继续向后遍历,由于cur之前的next已经被改变
        // 所以只能通过copy进行向后遍历
        cur=copy->next;
    }

步骤三:

去掉新链接,恢复旧链接 。

将所有新结点与旧结点的链接给去掉,恢复旧链表原本的链接,完成新结点的next链接。

具体图解:

具体代码:


// 步骤三
// 去掉新链接,恢复旧链接

    // 继续赋初值来进行遍历
    cur=head;

    // 定义两个结点,一个作为新链表的头结点
    // 一个作为新链表的尾结点
    // 当然,尾结点可以通过遍历进行链接操作
    struct Node* copyHead=NULL;
    struct Node* copyTail=NULL;

    // cur进行遍历操作
    while(cur)
    {
        // 第一次进入循环是给copy赋初值
        // 接下老就是遍历新结点的意思了
        struct Node* copy=cur->next;

        // 为了恢复旧链接,需要找到cur的next
        // 因此只能通过copy去找
        struct Node* next=copy->next;

        // 第一次进入循环copyTail肯定是NULL
        // 那么将copyHead和copyTail先赋初值copy
        // 因为copy就是新链表的头
        if(copyTail==NULL)
        {
            copyTail=copyHead=copy;
        }

        // 如果不是第一次进入循环,那么就要开始链接新结点了
        // 第二次再进来之后copyTail还是上一次copy的位置
        // 然而copy已经变成cur->next了也就是相对于新链表的
        // 下一个位置,因此直接copyTail->next=copy进行链接操作
        // 然后让copyTail也向后遍历copyTail=copy
        else
        {
            copyTail->next=copy;
            copyTail=copy;

        }


        // 链接旧结点
        cur->next=next;

        // 让旧结点向后遍历
        cur=next;



    }
    
    // 由于刚开始copyHead就是新链表的头,然后就没有再改变
    // 因此copyHead就是新链表的头,直接返回即可
    return copyHead;

 完整代码

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */

struct Node* copyRandomList(struct Node* head) 
{
	
    // 步骤一
    // 主要进行创建新结点copy(并给旧结点赋上对应新结点的值),并将新结点与旧结点进行链接

    // 给cur赋初值便于进行旧表结点的操作
    struct Node* cur=head;

    // 开始遍历
    while(cur)
    {
        // 定义一个next便于进行链接
        struct Node* next = cur->next;
        // 创建新结点copy,为新链表做基础
        struct Node* copy=(struct Node*)malloc(sizeof(struct Node));

        // 给新结点赋上对应旧结点的值
        copy->val=cur->val;

        // 链接操作

        // 旧结点链接新结点
        cur->next=copy;
        // 新结点链接旧结点
        copy->next=next;
        // 继续遍历
        cur=next;
    }

// 步骤二
// 目的是为了解决新结点random的链接问题

    // 重新赋值,重新遍历
    cur=head;

    // 遍历操作
    while(cur)
    {
        // 第一次进入循环是给copy赋初值
        // 接下老就是遍历新结点的意思了
        struct Node* copy=cur->next;

        // 如果对应旧结点的random!=NULL  
        // 然后根据旧结点的next刚好就是对应的新结点
        // 让新结点的random=旧结点的random的next
        if(cur->random!=NULL)
        {
            copy->random=cur->random->next;
        }
        // 如果cur->random是一个NULL
        // 那么毋容置疑copy->random一定是个NULL
        else
        {
            copy->random=NULL;
        }
        // cur继续向后遍历,由于cur之前的next已经被改变
        // 所以只能通过copy进行向后遍历
        cur=copy->next;
    }

// 步骤三
// 去掉新链接,恢复旧链接

    // 继续赋初值来进行遍历
    cur=head;

    // 定义两个结点,一个作为新链表的头结点
    // 一个作为新链表的尾结点
    // 当然,尾结点可以通过遍历进行链接操作
    struct Node* copyHead=NULL;
    struct Node* copyTail=NULL;

    // cur进行遍历操作
    while(cur)
    {
        // 第一次进入循环是给copy赋初值
        // 接下老就是遍历新结点的意思了
        struct Node* copy=cur->next;

        // 为了恢复旧链接,需要找到cur的next
        // 因此只能通过copy去找
        struct Node* next=copy->next;

        // 第一次进入循环copyTail肯定是NULL
        // 那么将copyHead和copyTail先赋初值copy
        // 因为copy就是新链表的头
        if(copyTail==NULL)
        {
            copyTail=copyHead=copy;
        }

        // 如果不是第一次进入循环,那么就要开始链接新结点了
        // 第二次再进来之后copyTail还是上一次copy的位置
        // 然而copy已经变成cur->next了也就是相对于新链表的
        // 下一个位置,因此直接copyTail->next=copy进行链接操作
        // 然后让copyTail也向后遍历copyTail=copy
        else
        {
            copyTail->next=copy;
            copyTail=copy;

        }


        // 链接旧结点
        cur->next=next;

        // 让旧结点向后遍历
        cur=next;



    }
    
    // 由于刚开始copyHead就是新链表的头,然后就没有再改变
    // 因此copyHead就是新链表的头,直接返回即可
    return copyHead;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁百万

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值