随机链表的深拷贝

时间:2024-03-27 13:02:03

目录

一、何为深拷贝?

二、题目

三、思路

1.拷贝节点插入到原节点后面

2.控制拷贝节点的random

3.脱离原链表 : 尾插的思想

四、代码

五、附加


一、何为深拷贝?

一个引用对象一般来说由两个部分组成:一个具名的Handle,也就是我们所说的声明(如变量)和一个内部(不具名)的对象,也就是具名Handle的内部对象。它在Manged Heap(托管堆)中分配,一般由新增引用对象的New方法是进行创建。深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等。

简而言之:

深拷贝就是拷贝一个一摸一样的链表出来。

二、题目

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

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

你的代码  接受原链表的头节点 head 作为传入参数。

示例 1:

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

示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

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

提示:

  • 0 <= n <= 1000
  • -104 <= Node.val <= 104
  • Node.random 为 null 或指向链表中的节点。

三、思路

1.拷贝节点插入到原节点后面


    while (cur)
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));     //每次都要开辟空间     //强制类型转换!
        copy->val = cur->val;   //赋值
            //链接
        struct Node* next = cur->next;  //定义一个next指针,可不考虑顺序问题
        cur->next = copy;
        copy->next = next;

        cur = next;     //cur 指针在原链表往后走!

    } 

2.控制拷贝节点的random

关键:不为空,random->next = cur->random->next;为空,random->next = NULL;

while (cur)
    {
        struct Node* copy = cur->next;          //不需要额外开辟空间,copy节点已经连接了原节点

        if (cur->random == NULL)
            copy->random = NULL;

        else        //cur->random要注意是不是空!
            copy->random = cur->random->next;   //copy的random指向的是拷贝节点


            //迭代      //copy不需要迭代,copy是封装在循环内的指针,由cur控制!
        cur = copy->next;      //cur 在原链表迭代!
                    
    }

3.脱离原链表 : 尾插的思想

介绍尾插:用尾插的思想实现移除链表中的元素-CSDN博客

尾插不是插入到原链表的尾部,而是里用尾插的思想!tail是尾节点!而不是尾节点的下一个!

while (cur)
    {
        struct Node* copy = cur->next;
        
        //脱离

        if(CopyHead == NULL)
        {
            CopyHead = tail = copy;     //tail是尾节点!而不是尾节点的下一个!
        }

        else
        {
            tail->next = copy;
            tail = tail->next;  //tail是尾节点!而不是尾节点的下一个!
        }

        if(copy)
            cur = copy->next;   //其实也可以不需要判断copy是否为空!(链表节点总数是偶数,所以cur只会是偶节点,copy不会为空!)

    }

四、代码

 //没必要弄清原链表的random指针指向哪个节点,只需要弄清原理即可!
    // 需要注意:malloc开辟新空间

struct Node* copyRandomList(struct Node* head) {

    //1.拷贝节点插入到原节点后面、

    struct Node* cur = head;    //没有哨兵位!

    while (cur)
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));     //每次都要开辟空间     //强制类型转换!
        copy->val = cur->val;   //赋值
            //链接
        struct Node* next = cur->next;  //定义一个next指针,可不考虑顺序问题
        cur->next = copy;
        copy->next = next;

        cur = next;     //cur 指针在原链表往后走!

    } 

    // 2.控制拷贝节点的random

    cur = head;     //重置cur
	
    while (cur)
    {
        struct Node* copy = cur->next;          //不需要额外开辟空间,copy节点已经连接了原节点

        if (cur->random == NULL)
            copy->random = NULL;

        else        //cur->random要注意是不是空!
            copy->random = cur->random->next;   //copy的random指向的是拷贝节点


            //迭代      //copy不需要迭代,copy是封装在循环内的指针,由cur控制!
        cur = copy->next;      //cur 在原链表迭代!
                    
    }


    //3.脱离原链表 : 尾插的思想
    cur = head;

    struct Node* CopyHead = NULL, *tail = NULL;

        //尾插的思想链接节点!
    while (cur)
    {
        struct Node* copy = cur->next;
        
        //脱离

        if(CopyHead == NULL)
        {
            CopyHead = tail = copy;     //tail是尾节点!而不是尾节点的下一个!
        }

        else
        {
            tail->next = copy;
            tail = tail->next;  //tail是尾节点!而不是尾节点的下一个!
        }

        if(copy)
            cur = copy->next;   //其实也可以不需要判断copy是否为空!(链表节点总数是偶数,所以cur只会是偶节点,copy不会为空!)

    }
 
    return CopyHead;

}

五、附加

头插也是一种常用的思想,常用来逆置,讲解如下:

题目:反转链表(头插与非头插)-CSDN博客