剑指offer 36. 复杂链表的复刻

简介: 剑指offer 36. 复杂链表的复刻

题目描述


请实现一个函数可以复制一个复杂链表。

在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。

注意

  • 函数结束后原链表要与输入时保持一致。


数据范围

链表长度 [0,500]。


方法一:链表 O(n)


这道题其实和普通的链表复制那题比较相似,只是要额外处理一个随机指针:

  1. 先将链表所有结点复制一遍,即在原链表中的每个结点都复制一个新的结点。
  2. 处理所有 random 指针,从前往后遍历一遍,将复制出来的结点的 random 指针指向对应结点。
  3. 将两个链表拆开。

我们来举个例子:

1b2436661b43470fbe54b333aa973770.png


第一步: 将链表所有结点复制一遍。

第二步: 处理所有 random 指针。

第三步: 再将链表拆开。

7ce2b228d6994920a360aab31498d94c.png

/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* copyRandomList(ListNode* head) {
        //先复制一遍结点
        for (auto p = head; p;)
        {
            auto np = new ListNode(p->val);
            np->next = p->next;
            p->next = np;
            p = np->next;
        }
        //再改变所有random指针
        for (auto p = head; p; p = p->next->next)
        {
            if (p->random != NULL)
                p->next->random = p->random->next;
        }
        //开始将两个链表拆开
        auto dummy = new ListNode(-1);
        auto cur = dummy;
        for (auto p = head; p; p = p->next)
        {
            cur->next = p->next;
            cur = cur->next;
            p->next = p->next->next;
        }
        return dummy->next;
    }
};


方法二:哈希表 O(n)

我们可以用哈希表来存储链表中的每个结点,然后串起来:


如果当前结点或当前结点的 random 结点没有被复制过,就复制出来放在哈希表中。

将新链表的指针指向的结点的 next 指针和 next->random 指针指向哈希表中复制的结点。

将两个链表的指针后移一位。

我们还是拿上面那个例子看看:

098154b504974aa89af22d31a306be93.png

第一步: 建设一个空的头结点,用来去连接后面新创的结点。

第二步: 创建第一个结点 7 和它 random 指向的结点,将创建的结点位置用哈希表存起来,这样下次用到这个结点时就直接取,不用再创建。

d6a7bb41316f40e2bd9c258b9463c117.png


第三步: 创建第二个结点,但是第二个结点 random 指向的结点 7 已经被创建过,所以直接在哈希表中查找即可。

e4c02f877f65408fa05cd02154f4b998.png



第四步: 第三个结点也被创建过,所以只用创建其 random 指向的结点 1 。

522075d406eb48eb99e29e07ea80b8ef.png



第五步: 第四个结点没被创建,但是其 random 指向的结点 1 已经被创建,直接使用即可。


0db12180cef644d58b58e31b229cbe78.png


第六步: 所需结点都已经被创建,直接连接结点即可,得到最终结果。

/*/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* copyRandomList(ListNode* head) {
        unordered_map<ListNode*, ListNode*> hash;
        hash[NULL] = NULL;
        auto dummy = new ListNode(-1), tail = dummy;
        while (head)
        {
            //将没有复制过的结点进行复制,放到哈希表中
            if (!hash.count(head))   hash[head] = new ListNode(head->val);
            if (!hash.count(head->random))   hash[head->random] = new ListNode(head->random->val);
            tail->next = hash[head];
            tail->next->random = hash[head->random];
            tail = tail->next;
            head = head->next;
        }
        return dummy->next;
    }
};


欢迎大家在评论区交流~

目录
相关文章
|
7月前
《剑指offer》——合并两个排序的链表
《剑指offer》——合并两个排序的链表
|
2月前
(剑指offer)18、删除链表的节点—22、链表中倒数第K个节点—25、合并两个排序的链表—52、两个链表的第一个公共节点(2021.12.07)
(剑指offer)18、删除链表的节点—22、链表中倒数第K个节点—25、合并两个排序的链表—52、两个链表的第一个公共节点(2021.12.07)
52 0
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
54 5
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 18. 删除链表的节点
Leetcode题目"剑指 Offer 18. 删除链表的节点"的Python解决方案,通过使用双指针法找到并删除链表中值为特定数值的节点,然后返回更新后的链表头节点。
45 4
|
7月前
|
Java C语言
剑指offer(牛客)——合并两个排序的链表
剑指offer(牛客)——合并两个排序的链表
40 1
|
7月前
|
存储 Java C语言
剑指offer(牛客)——从尾到头打印链表
剑指offer(牛客)——从尾到头打印链表
42 1
|
7月前
【一刷《剑指Offer》】面试题 17:合并两个排序的链表
【一刷《剑指Offer》】面试题 17:合并两个排序的链表
|
7月前
【一刷《剑指Offer》】面试题 16:反转链表
【一刷《剑指Offer》】面试题 16:反转链表
|
7月前
【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点
【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点
|
7月前
|
机器学习/深度学习
【一刷《剑指Offer》】面试题 13:在 O(1) 时间删除链表结点
【一刷《剑指Offer》】面试题 13:在 O(1) 时间删除链表结点