复制带随机指针的链表

来自力扣的一道题目:138. 复制带随机指针的链表 - 力扣(LeetCode)

 问题描述:

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

中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其

对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原

链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的

节点 。

示例1:

 

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例二:

 

 

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

对应的链表结构:

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

 问题分析:

这道题的主要问题是将带随机链表的指针拷贝一份,并返回拷贝链表的头节点,这里有些人就会心

生想法,直接把原来的那个链表的头节点返回不就可以了吗?但很显然是过不了的,因为我们要返

回的头节点地址与原链表的头节点地址应该是不同的,如果是相同的,那还能叫拷贝吗?

那么我们应该如何拷贝呢

如果我们创建一个新链表来进行拷贝的话,那么对应的随机指针和next指针有应该如何来存储,如

果直接进行赋值的话,因为对应的指针是不变的,所以相当于再次应用了原链表的指针,显然还是

行不通的。

正确办法有点不好想,在这我也只能直接讲,大家最好能有自己的理解和思考,具体分为三步:

1.在原节点的后面插入拷贝节点

 这一步我们先不考虑random指针的关系,先进行链表的插入,将每个拷贝节点放在每个原节点的

后面,而拷贝的节点我们需要自己malloc,在拷贝之前要先把原链表的下一个节点存储下来,以此

来作为迭代进行插入拷贝节点。

2.拷贝节点的random指针,原节点的random->next。

 

如上图,对拷贝节点的random进行连接时,必须要将拷贝节点链接到拷贝节点上,切忌链接到原

节点上,会成为环形链表,同过上面的图示,我们应该不难理解了,连接应该是:

copy->random = cur->random->next(对应的随机节点的拷贝节点)。

当然,对于cur->random==NULL的,应该将copy->random=NULL。这是一种特殊情况。

3.将拷贝节点剪下来,链接成新链表,并回复原链表。

 如上图,是这一步进行的步骤,首先将拷贝节点截取下来并尾插,其次让原链表还原,即

cur->next = cur->next->next;

尾插是要初始化两个节点,用来记录头和尾。

代码实现:

  1.在原节点的后面插入拷贝节点

//插入拷贝节点
   struct Node* cur = NULL;
   cur= head;
   while(cur)
   {
      struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
      copy->val = cur->val;
      struct Node* next = cur->next;

      //拷贝
      cur->next = copy;
      copy->next = next;
      
      cur = next;

   }

2.拷贝节点的random指针,原节点的random->next。

//拷贝节点的random
   cur = head;
   
   while (cur)
  {
	struct Node* copy = cur->next;
	if (cur->random == NULL)
	{
		copy->random = NULL;
	}
	else
	{
		copy->random = cur->random->next;
	}

	cur = cur->next->next;
   }

3.将拷贝节点剪下来,链接成新链表,并回复原链表。

 //尾插拷贝链表
   struct Node* copyhead =NULL,*copytail = NULL;
   cur = head;
  
   while(cur)
   {
       struct Node* copy = cur->next;
       struct Node* next = copy->next;
       //尾插
       if(copyhead == NULL)
       {
           copyhead = copytail = copy;
       }
       else
       {
           copytail->next = copy;
           copytail = copytail->next;
       }
       //恢复原链表
       cur->next = next;

       cur = next;
   }

整体代码:

struct Node* copyRandomList(struct Node* head)
{
    //插入拷贝节点
    struct Node* cur = NULL;
    cur = head;
    while (cur)
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        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 == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;
        }

        cur = cur->next->next;
    }

    //尾插拷贝链表
    struct Node* copyhead = NULL, * copytail = NULL;
    cur = head;

    while (cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        //尾插
        if (copyhead == NULL)
        {
            copyhead = copytail = copy;
        }
        else
        {
            copytail->next = copy;
            copytail = copytail->next;
        }
        //恢复原链表
        cur->next = next;

        cur = next;
    }
    return copyhead;
}

这道题是链表里面比较难搞的题,当大家真正理解了这道题之后,说明我们的链表就可以了!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值