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

要求返回这个链表的深拷贝

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

示例:

  1. 输入:
  2. {"$id":"1","next":{"$id":"2","next":null,"random":{"$ref":"2"},"val":2},"random":{"$ref":"2"},"val":1}
  3. 解释:
  4. 节点 1 的值是 1,它的下一个指针和随机指针都指向节点 2
  5. 节点 2 的值是 2,它的下一个指针指向 null,随机指针指向它自己。

提示:

  1. 你必须返回给定头的拷贝作为对克隆列表的引用。

Note:

  1. You must return the copy of the given head as a reference to the cloned list.

解题思路:

由于需要考虑随机指针,随机指针指向的节点可能是null,也可能是链表的最后一个节点,深拷贝下,你不能将新链表的随机指针指向原链表的节点,所以无法一遍得到新链表。

两种解题方法,一种是拷贝所有节点,暂存在一种数据结构内,然后再遍历一遍该数据结构,找到拷贝的节点,确定随机指针的指向。因为每个节点都要找到随机指针指向的节点,如果借助 散列表(字典) 其查找复杂度为 O(1) ,这样可以把总时间复杂度降到 O(n) ,由于要暂存所有节点,其空间复杂度为 O(n)。

另一种解题方法是需要三次遍历得到结果,简单来说就是先把每个节点拷贝,并把拷贝节点连接到原节点后面,依次类推。确定随机节点的关系之后再拆分链表。是一种很巧妙的思路:

原链表:1->2->3

复制每个节点到原节点后面:1->1->2->2->3->3

复制随机指针的指向关系

拆分链表:1->2->3, 1->2->3

复制随机指针指向时,原节点的随机节点下一个节点即为新节点的随机节点。假如原链表:1->2->3,节点1的随机节点为3,复制节点之后:1->1->2->2->3->3

我们只需将新节点(原节点1的后一个节点1)指向原节点的随机节点的后一个节点(原节点的随机节点为3,而复制之后随机节点3的后一个节点依然为3,但这个节点为新节点),最后拆分链表(链表拆分不影响节点指向关系)。其时间复杂度为 O(n),空间复杂度为 O(1)。

第一种方法:

Java:

  1. class Solution {
  2. public Node copyRandomList(Node head) {
  3. if (head == null) return null;
  4. HashMap<Node, Node> map = new HashMap<>();//借助hashMap
  5. Node newHead = new Node(0);//虚拟头节点
  6. Node cur = newHead;//指针指向当前节点
  7. Node tmp;
  8. while (head != null) {
  9. if (map.containsKey(head)) {//查询原节点是否已存在于map
  10. tmp = map.get(head);//如果存在直接取value值
  11. } else {
  12. tmp = new Node(head.val);//不存在则新建一个值相同的节点
  13. map.put(head, tmp);//存入map,key为原节点,value为新节点
  14. }
  15. cur.next = tmp;
  16. if (head.random != null) {//原节点的随机节点不为空的情况下
  17. if (map.containsKey(head.random)) {//查询该随即节点是否已存在于map
  18. tmp.random = map.get(head.random);//存在则直接将新节点的随机指针指向其value值
  19. } else {
  20. tmp.random = new Node(head.random.val);//不存在则新建一个值相同的随机节点
  21. map.put(head.random, tmp.random);//存入map,key为原节点的随机节点,value为新节点的随机节点
  22. }
  23. }
  24. head = head.next;//刷新原链表当前头节点
  25. cur = tmp;//即cur=cur.next,刷新新链表当前节点
  26. }
  27. return newHead.next;
  28. }
  29. }

Python3:

  1. class Solution:
  2. def copyRandomList(self, head: 'Node') -> 'Node':
  3. if not head: return None
  4. map = {}
  5. newHead = Node(val=0, next=None, random=None)
  6. cur = newHead
  7. while head:
  8. if head in map.keys():
  9. tmp = map.get(head)
  10. else:
  11. tmp = Node(val=head.val, next=None, random=None)
  12. map.setdefault(head, tmp)
  13. cur.next = tmp
  14. if head.random:
  15. if head.random in map.keys():
  16. tmp.random = map.get(head.random)
  17. else:
  18. tmp.random = Node(val=head.random.val, next=None, random=None)
  19. map.setdefault(head.random, tmp.random)
  20. head = head.next
  21. cur = tmp
  22. return newHead.next

第二种方法:

Java:

  1. class Solution {
  2. public Node copyRandomList(Node head) {
  3. if (head == null) return null;
  4. //复制节点 1->2->3 ==> 1->1->1->2->2->3->3
  5. Node cur = head;
  6. while (cur != null) {
  7. Node next = cur.next;
  8. Node tmp = new Node(cur.val);
  9. cur.next = tmp;
  10. tmp.next = next;
  11. cur = next;
  12. }
  13. //复制随机指针
  14. cur = head;
  15. while (cur != null) {
  16. //cur.next.random=>新节点的随机节点 cur.random.next=>原节点的随机节点的下一个节点
  17. cur.next.random = cur.random == null ? null : cur.random.next;
  18. cur = cur.next.next;//链表扩展一倍后肯定是偶数位链表,所以可以直接用cur.next.next
  19. }
  20. //分离节点
  21. cur = head;
  22. Node newHead = head.next;//新链表头节点
  23. while (cur != null) {
  24. Node tmp = cur.next;
  25. cur.next = tmp.next;
  26. cur = cur.next;
  27. tmp.next = cur == null ? null : cur.next;
  28. }
  29. return newHead;
  30. }
  31. }

Python3:

  1. class Solution:
  2. def copyRandomList(self, head: 'Node') -> 'Node':
  3. if not head: return None
  4. cur = head
  5. while cur:
  6. next = cur.next
  7. tmp = Node(val=cur.val, next=next, random=None)
  8. cur.next = tmp
  9. cur = next
  10. cur = head
  11. while cur:
  12. cur.next.random = cur.random.next if cur.random else None
  13. cur = cur.next.next
  14. cur = head
  15. newHead = head.next
  16. while cur:
  17. tmp = cur.next
  18. cur.next = tmp.next
  19. cur = cur.next
  20. tmp.next = cur.next if cur else None
  21. return newHead

LeetCode 138:复制带随机指针的链表 Copy List with Random Pointer的更多相关文章

  1. [Java]LeetCode138. 复制带随机指针的链表 | Copy List with Random Pointer

    A linked list is given such that each node contains an additional random pointer which could point t ...

  2. 复制带随机指针的链表 · Copy List with Random Pointer

    [抄题]: 给出一个链表,每个节点包含一个额外增加的随机指针可以指向链表中的任何节点或空的节点. 返回一个深拷贝的链表. [思维问题]: [一句话思路]: 完完全全地复制,否则不好操作. 1-> ...

  3. Java实现 LeetCode 138 复制带随机指针的链表

    138. 复制带随机指针的链表 给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点. 要求返回这个链表的 深拷贝. 我们用一个由 n 个节点组成的链表来表示输入/ ...

  4. [LeetCode]138复制带随机指针的链表

    题目描述: 给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点. 要求返回这个链表的深度拷贝. 思路: 先遍历链表,将每个节点对应的随机指针指向的对象利用Hash ...

  5. LeetCode 138——复制带随机指针的链表

    1. 题目 2. 解答 第一次遍历链表的时候,复制旧链表的节点值建立一个新的链表,同时定义一个 unordered_map 作为哈希表,哈希表的键为旧链表的节点指针,值为新链表的节点指针. 然后,第二 ...

  6. Leetcode 138. 复制带随机指针的链表

    1.题目要求 给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点. 要求返回这个链表的深度拷贝. 2.解题思路 (1)笔试思路(求速度,拿分数):使用哈希表 /* ...

  7. 【leetcode 138. 复制带随机指针的链表】解题报告

    方法一:递归 unordered_map<Node*,Node*> dict; Node* copyRandomList(Node* head) { if (!head) return h ...

  8. LintCode-105.复制带随机指针的链表

    复制带随机指针的链表 给出一个链表,每个节点包含一个额外增加的随机指针可以指向链表中的任何节点或空的节点. 返回一个深拷贝的链表. 挑战 可否使用O(1)的空间 标签 哈希表 链表 优步 code / ...

  9. 【LeetCode】138. Copy List with Random Pointer 复制带随机指针的链表 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人公众号:负雪明烛 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https:/ ...

随机推荐

  1. java集合框架collection(3)Set、List和Map

    Set.List和Map是java collection中最常用的三种数据结构. Set是集合,不允许有重复的元素,List是动态数组实现的列表,有序可重复,Map是key-value的键值对,用于快 ...

  2. serverless 项目配置及创建helloworld应用(二)

    阅读目录 一:学习使用AWS Lambda来作为服务器引擎 二:使用serverless环境搭建 三:创建我们的第一个应用,hello world 服务 回到顶部 一:学习使用AWS Lambda来作 ...

  3. 使用wireshark捕获SSL/TLS包并分析

    原创博客,转载请注出处! TLS运作方式如下图:

  4. Java多线程(三):Synchronized

    多线程安全 脏读:多个线程对同一个对象的实例变量进行修改后访问,导致读到的数据是被修改过的. 实例 ThreadDomain16类 public class ThreadDomain16 { priv ...

  5. js api 之 fetch、querySelector、form、atob及btoa

    js api 之 fetch.querySelector.form.atob及btoa 转载请注明出处: https://www.cnblogs.com/funnyzpc/p/11095862.htm ...

  6. 系统学习 Java IO (十五)----字符读写 Reader/Writer 其他子类

    目录:系统学习 Java IO---- 目录,概览 跟踪行号的缓冲字符输入流 LineNumberReader LineNumberReader 类是一个 BufferedReader ,用于跟踪读取 ...

  7. git push 时:报missing Change-Id in commit message footer的错误

    1. 一般而言,按照提示执行以下两个命令即可生成新的Change-id - gitdir=$(git rev-parse --git-dir); scp -p -P 29418 guan@192.16 ...

  8. 六种 主流ETL 工具的比较(DataPipeline,Kettle,Talend,Informatica,Datax ,Oracle Goldengate)

    六种 主流ETL 工具的比较(DataPipeline,Kettle,Talend,Informatica,Datax ,Oracle Goldengate) 比较维度\产品 DataPipeline ...

  9. java获取Timestamp类型的当前系统时间

    java获取取得Timestamp类型的当前系统时间 java获取取得Timestamp类型的当前系统时间 格式:2010-11-04 16:19:42 方法1: Timestamp d = new ...

  10. Django框架rest_framework中APIView的as_view()源码解析、认证、权限、频率控制

    在上篇我们对Django原生View源码进行了局部解析:https://www.cnblogs.com/dongxixi/p/11130976.html 在前后端分离项目中前面我们也提到了各种认证需要 ...