注:本文的代码实现使用的是 JS(JavaScript),为前端中想使用JS练习算法和数据结构的小伙伴提供解题思路。

描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。


示例:

输入:{1,2,3,4,5,3,5,#,2,#}
输出:{1,2,3,4,5,3,5,#,2,#}

解析: 我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。
以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5
后半部分,3,5,#,2,#分别的表示为
1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null
如下图:


解题思路

直接法

首先按照正常链表的思路复制出来一个新的链表,其中pHead是题中给出需要复制的链表的头节点。RandomListNode是创建节点的类,其结构如下:

function RandomListNode(x){// label 相当于普通链表的 valthis.label = x;// 当前节点的下一个节点this.next = null;// 当前节点所指向的随机节点this.random = null;
}

复制链表的代码如下,新链表的头节点为newHead

 // 复制头节点指针,用于遍历链表// 如果直接用 pHead 进行遍历,后续再想找到原始链表的头节点就找不到了let p = pHead// 手动创建一个头节点,也就是新的链表的头节点let newHead = new RandomListNode(p.label)// 新的链表的节点指针,用于遍历let p_newHead = newHeadwhile(p.next){p_newHead.next = new RandomListNode(p.next.label)p = p.nextp_newHead = p_newHead.next}

至此,我们的工作可以看作是完成了一半,剩下的就是把新链表中每个节点的random指针指向对应的节点即可。那么我们该如何去做呢?

解决方案肯定不止一种,本人想到了一种思路:因为链表是有序的,因此,我们可以根据索引,去寻找这个关系。

以题中给的示例说明,原始链表中,D的random指针指向的是B,而B在整个链表中的索引是1(索引从0开始),因此在我们创建的新的链表中,让D’(新链表中根据原始链表中D创建的节点)指向新创建节点的第1个节点即可。

所以,创建了一个新的函数find去查找节点noderandom所指向节点在其所在链表中的索引,结构如下:

function find(node, pHead){// node 节点的 random 指针指向空, 则索引为 nullif(!node.random) return null// 记录索引的变量let index = 0while(pHead){// 如果当前节点和node 节点的 random 指向的是同一个节点,则返回 indexif(pHead === node.random) return indexpHead = pHead.nextindex ++}// 此处无需进行其他 return 因为一定能找到索引的
}

最后本人创建了两个变量:

// 用于存放索引的数组,初始化的时候,将头节点的 random 指向节点的index放入
let list = [find(pHead, pHead)]
// 用于存放新创建链表的每个节点的指针,方便给新节点的 random 赋值
let node_pointer = [p_newHead]

完整的代码如下:

function find(node, pHead){if(!node.random) return nulllet index = 0while(pHead){if(pHead === node.random) return indexpHead = pHead.nextindex ++}
}
function Clone(pHead)
{// 头节点为 null ,则直接返回即可if(!pHead) return pHeadlet p = pHeadlet newHead = new RandomListNode(p.label)let p_newHead = newHeadlet list = [find(pHead, pHead)]let node_pointer = [p_newHead]while(p.next){p_newHead.next = new RandomListNode(p.next.label)// 在创新新链表的时候,顺便按顺序把原始链表每个节点的 random 所指向节点的索引记录list.push(find(p.next, pHead))// 也顺便存放新创建链表的每个节点的指针node_pointer.push(p_newHead.next)p = p.nextp_newHead = p_newHead.next}p_newHead = newHeadlet count = 0while(p_newHead){// list[count] 是当前节点random 指向节点的索引// 然后再从 node_pointer 找到对应的指针即可p_newHead.random = node_pointer[list[count]]// 为了清晰,把这句话单独拿出来了,其实可以合并到上一句代码中count++p_newHead = p_newHead.next}return newHead
}

这个方法可能看着乱,但这是最直观,最容易理解的,时间复杂度是O(n2)O(n^2)O(n2)的。其实,你可以直接return pHead的,也能通过,就看面试官办不办你了,哈哈哈哈。

原地复制法

  1. 把原始链表中的每个节点复制,并按下图的方式串接起来,只需要给新创建的节点指定label和next即可

    代码如下:
    let p = pHeadwhile(p){const temp = p.next// 创建节点并指定labelp.next = new RandomListNode(p.label)// 为新创建的节点指定nextp.next.next = tempp =  temp}
  1. 把复制的结点的random指针指向被复制结点的random指针的下一个结点
    p = pHeadwhile(p){// 保证当前节点的 random 非空,若为空,就不用管了if(p.random)p.next.random = p.random.nextp = p.next.next}
3. 拆分成两个链表,具体拆分方式可以看下面代码,跟着代码走一两个循环就能明白了

     p = pHead// 新的链表的头节点let newHead = p.next// 用于遍历新的链表的指针let p_newHead = newHead// 到倒数第二个节点结束,本题中是 E ,否则在 p = p.next 的时候会报错while(p.next.next){p.next = p_newHead.nextp = p.nextp_newHead.next = p.nextp_newHead = p.next}// 如果这句话不加上,虽然输出的 newHead 没问题,但是原始链表最后一个节点的复制节点(E')也会挂在E的后面p.next = null

完整代码如下:

function Clone(pHead)
{if(!pHead) return pHeadlet p = pHeadwhile(p){const temp = p.nextp.next = new RandomListNode(p.label)p.next.next = tempp =  temp}p = pHeadwhile(p){if(p.random)p.next.random = p.random.nextp = p.next.next}p = pHeadlet newHead = p.nextlet p_newHead = newHeadwhile(p.next.next){p.next = p_newHead.nextp = p.nextp_newHead.next = p.nextp_newHead = p.next}p.next = nullreturn newHead
}

剑指 Offer JZ35 复杂链表的复制相关推荐

  1. 【LeetCode】剑指 Offer 35. 复杂链表的复制

    [LeetCode]剑指 Offer 35. 复杂链表的复制 文章目录 [LeetCode]剑指 Offer 35. 复杂链表的复制 package offer;import java.util.Ar ...

  2. 剑指 Offer 35. 复杂链表的复制

    剑指 Offer 35. 复杂链表的复制 题目 题目链接 解题思路 题目理解 解题思路 具体代码 题目 题目链接 https://leetcode-cn.com/problems/fu-za-lian ...

  3. 《剑指offer》-- 复杂链表的复制、字符串的排列、数组中出现次数超过一半的数字、连续子数组的最大和

    一.复杂链表的复制: 参考牛客网的chancy:https://www.nowcoder.com/questionTerminal/f836b2c43afc4b35ad6adc41ec941dba 1 ...

  4. 【三次优化】剑指 Offer 35. 复杂链表的复制

    立志用最少代码做最高效的表达 请实现 copyRandomList 函数,复制一个复杂链表.在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意 ...

  5. 剑指offer——35复杂链表的复制

    这题很是巧妙. 突破了常规思维. 竟然可以把传入进来的链表和复制的链表链在一起.然后再算出slibling指针.最后在分离. 直接把空间复杂度变为O(1)了. 很巧妙,很实用. 题目: 请实现函数Co ...

  6. 剑指 Offer 35. 复杂链表的复制(哈希/衍生拆分图解)

    题目描述 请实现 copyRandomList 函数,复制一个复杂链表.在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null. ...

  7. LeetCode笔记】剑指 Offer 35. 复杂链表的复制(Java、哈希表、原地算法)

    文章目录 题目描述 思路 && 代码 1. 哈希表法 2. 原地算法 二刷 题目描述 主要有两个考虑点: 不能改变原链表 新链表赋予 next.random 时,复制结点不一定存在 思 ...

  8. 【算法】剑指 Offer 35. 复杂链表的复制 【重刷】

    1.概述 地址:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/ 请实现 copyRandomList 函数,复制一个复 ...

  9. 剑指Offer之复杂链表的复制

    题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否 ...

最新文章

  1. canal下载 linux_canal实时同步mysql数据到redis或ElasticSearch
  2. Java实用教程笔记 常用实用类
  3. 微软Office Online服务安装部署及wopi代码实现--------Office Online服务器的安装
  4. 如何把HTML转换成动图,html5实现图片转圈的动画效果——让页面动起来
  5. 深入理解javascript函数参数
  6. 【qduoj - 142】 多重背包(0-1背包的另类处理,dp)
  7. centos 限制只能访问某个目录的php文件
  8. 百度人脸识别 人脸识别模型_当我说人脸识别很容易时,他们笑了。 但是可以。...
  9. 最长不下降子序列的长度
  10. DORADO展现中间件的实现
  11. List工具类之List集合去重
  12. curl命令查询IP和运营商
  13. 回溯法——最大团问题c
  14. 使用腾讯云轻量应用服务器搭建网络质量拨测工具 SmokePing
  15. 4月刊佳文推荐:开发者的眼界
  16. 《大数据时代》读书笔记——知道“是什么”就够了,没必要知道“为什么”。我们不必非得知道现象背后的原因,而是要让数据自己“发声”
  17. 第五日 高手篇:信用证全攻略
  18. 数据库-Oracle
  19. 数据结构(C语言版)——顺序栈(代码版)
  20. Python正则表达式 re

热门文章

  1. Java内部类最细详解
  2. python解决单调栈问题
  3. python中datetime函数怎么获得当年年份_Python 日期和时间函数使用指南
  4. 《C++游戏开发》笔记十四 平滑过渡的战争迷雾(二) 实现:真正的迷雾来了
  5. Android 数据Parcel序列化过程源码分析
  6. GlassFish下手动部署JSF程序
  7. ecmall ajax,ajax
  8. 3分钟学会python_3分钟学会使用Python推荐系统库Surprise
  9. python dd 合并二进制文件_马克的Python学习笔记#数据编码与处理 5
  10. win10恢复经典开始菜单_window10开始菜单经典怎么设置