赋值带随机指针的链表 - (c 语言)

目录

题目描述

 思路:

具体代码实现:


题目描述

给你一个长度为 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 作为传入参数。

 思路:

  这个题目的难点在于 , random 指针的复制,比如上述的例子,第二个结点 " 13 " 的 random 指针 " 7 " 这个结点,那么我们在新的链表空间中,如果和去找到这个 原链表 " 7 "  对于新链表的 " 7 " 这个位置。

我们不能按照 值 去查找,比如上述中 我们想要找到 值为 " 7 " 这个结点,因为 链表中可能有多个 值为 " 7 " 的结点。如果按照地址去查询也是不行的,因为我们只能通过循环 在原链表中找到对应结点的地址,但是我们是在新的链表中进行random指针的查找,新的链表和 原链表地址是不同的。

如果我们想要按照 地址去查找,就只能通过相对地址来寻找,但是很麻烦,简单俩说就是,如上例子,我们想要找到 13 的random 指针位置,13 的random 指针指向的位置是 13 这个结点的 -1 的相对位置; 11 这个结点的 random指针指向位置就是 1 这个结点,1 这个结点 的相对位置就是 11 的 2 的这个相对位置。

 或者说是,找到 在新链表中 位于第几个 结点,虽然地址 ,新链表和 原链表对不上,但是从起始位置开始数的个数,是对应上的。用这种方法,时间复杂度是 O(n^2)。

或者我们可以创建两个指针数组,一个按照链表顺序存储 原链表 各个结点的地址,一个存储 新链表结点地址,然后我们在 原链表中 找到某一个结点的random 所在 链表的位置,这个位置也就对应这个数组的下标,那么我们可以在 存储 新链表结点 的数组 同一下标位置找到 新链表当中 这个结点 的 random 指针链表当中的位置,如下图所示:

 下标是 可以视为 这个结点在链表当中的相对位置。但是这个方式只是比之前快一点点,不能优化到 O(n)。

更优解是:

我们在原链表的每一个结点后,都创建一个新的结点,这个结点和前一个结点的值相同,如下图所示(拷贝结点链接到源节点的后面):

 那么我们之前的难点是在,我们在 新链表的当中找不到 结点的 random 指向的地址,那么现在我们就可以找到了,如上述,红色的 结点就是我们的 新链表的结点,红色的 13 这个结点的 random指针 就应该 红色 7  这个结点,而 7 这个结点的位置就在 原链表 7 这个结点的后面,那么同样的,其他的结点也是一样的,都是在 原链表 random 指针指向结点的后一个结点。

然后就可以通过上述关系来把红色结点,也就是新的链表结点 的 random 指针的进行修改。

最后就是 把 拷贝结点解下来连接成一个新的链表,把原链表恢复。

具体代码实现:

struct Node* copyRandomList(struct Node* head) {
	struct Node* cur = head;
    while(cur)
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;

        //修改链表当中的链接关系
        struct Node* tmp = cur->next;
        cur->next = copy;
        copy->next = tmp;

        // cur 往后迭代
        cur = tmp;
    }

    // 处理 结点的 random 指针
    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;
        if(cur->random == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;
        }
        cur = copy->next;
    }

    // 把 拷贝结点链接为新的链表,把原链表恢复
    struct Node* copyhead = NULL, *copyTail = NULL;
    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        
        // copy 尾插
        if(copyhead == NULL)
        {
            copyhead = copyTail = copy;
        }
        else
        {
            copyTail->next = copy;
            copyTail = copyTail->next;
        }

        // 恢复原链表
        cur->next = next;

        // cur 跌倒往下走
        cur = next;
    }

    return copyhead;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

chihiro1122

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

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

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

打赏作者

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

抵扣说明:

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

余额充值