复杂链表的复制

  • 题目:实现一个函数complexListNode 复制一个复杂链表。在链表中,每个节点除了有一个next指针指向下一个节点,还有另外一个before节点,before节点指向链表中任意一个节点,或者null节点。
  • 链表节点定义使用之前文章 数据结构与算法–链表实现以及应用 中链表节点的定义,以及链表中其他方法实现都沿用以上文章中有详细的说明。
  • 如下节点定义:
public class ListNode implements Comparable<ListNode> {private Integer value;private ListNode next;private ListNode before;
......
}
  • 如下图中包含的5个阶段的复杂链表,图中实线箭头表示next指针,虚线表示before指针,简单画出如下,我们省略了null指针没有画出来:

  • 分析:

    • 方法一:如上链表的结构看最简单的方式先遍历一次,将节点复制,并且用next 链接节点,得到基础结构
    • 在循环查找每一个节点的before节点,因为此处before节点是随机指向,可能指向本节点之前的节点,因此每次都需要遍历一次链表来查找对应的节点信息
    • 时间复杂度O(N2)
    • 方法二:为了避免before节点查找的复杂性,先遍历一次oldList,复制next指针,并且将oldNode,newNode存储到map中,
    • 第二次遍历,此时我们已经知道了oldNode~ newNode的对应关系,我们在查找到oldList的before指向的时候,直接从map中获取到对应newNode,这样通过O(1) 时间获取到before的值
    • 此处空间换时间的方法
    • 方法三:换一种思路,能否不用辅助空间情况下来实现O(n)的时间复杂度,
    • 第一次遍历,直接将访问到的节点复制新节点,并且插入到访问到的节点后面,如下图:
    • 第二次遍历,设置新建节点的before节点,因为每个新节点A‘都在老节点A的next位置,所以,老节点A 的before C节点的next 就是改节点的新节点C’ 那么新节点A‘ 的before节点就是 C’
    • 第三次遍历,将奇数项的节点单独拆分成一个链表得到我们复制的链表,偶数位项就是老的链表
  • 根据如上分析有如下代码:

/*** 构建复杂链表,链表next节点指向下一个节点,* before节点指向链表中任意节点* */public static ListNode buildComplexListNode(){Random random = new Random();ListNode pHead = new ListNode(random.nextInt(100));ListNode[] nodeArray = new ListNode[20];for (int i = 0; i < 20; i++) {ListNode newNode = new ListNode(random.nextInt(100));nodeArray[i] = newNode;ListNode headNode = pHead;while (headNode.getNext() !=  null){headNode = headNode.getNext();}headNode.setNext(newNode);}print(pHead);ListNode  headNode = pHead;while (headNode.getNext() != null){headNode.setBefore(nodeArray[random.nextInt(nodeArray.length -1)]);headNode = headNode.getNext();}return pHead;}/*** 复制复杂链表* 方法一:空间换时间,* 先遍历一次oldList,复制next指针,并且将oldNode,newNode存储到map中,* 第二次遍历同时遍历oldList,newList,直接通过oldList的before指向的oldListNode从mep中获取newNode,* O(1)时间获取到before值* */public static ListNode complexListNode(ListNode pHead){if(pHead == null || pHead.getNext() == null){return pHead;}Map<ListNode, ListNode> oldToNewNode = new HashMap<>();ListNode complexNode = pHead;ListNode newHead = null;ListNode myHeadNode = null;while (complexNode != null){ListNode createNode = new ListNode(complexNode.getValue());oldToNewNode.put(complexNode, createNode);if(newHead == null){newHead = createNode;myHeadNode = newHead;}else {myHeadNode.setNext(createNode);myHeadNode = myHeadNode.getNext();}complexNode = complexNode.getNext();}ListNode beforeComplexNode = pHead;ListNode beforeSetNode = newHead;while (beforeComplexNode.getNext() != null && beforeSetNode.getNext() != null){if(oldToNewNode.containsKey(beforeComplexNode.getBefore())){beforeSetNode.setBefore(oldToNewNode.get(beforeComplexNode.getBefore()));}beforeComplexNode = beforeComplexNode.getNext();beforeSetNode = beforeSetNode.getNext();}return newHead;}
/*** 复制复杂链表* 方法二:* 第一次遍历,直接将范问到的节点复制新节点,并且插入到范问到的节点后面* 第二次遍历,设置新建节点的before节点,因为每个新节点A‘都在老节点A的next位置,所以,老节点的before B节点的next 就是改节点的新节点B’* 那么新节点A‘的before节点就是B’* 第三次遍历,将奇数项的节点单独拆分成一个链表得到我们复制的链表,偶数位项就是老的链表*/public static ListNode complexListNode_2(ListNode pHead){if(pHead == null || pHead.getNext() == null){return pHead;}ListNode compleNode = pHead;while (compleNode.getNext() != null){ListNode createNode = new ListNode(compleNode.getValue());createNode.setNext(compleNode.getNext());compleNode.setNext(createNode);compleNode = compleNode.getNext().getNext();}ListNode beforeCompleNode = pHead;while (beforeCompleNode.getNext() != null){if(beforeCompleNode.getBefore() != null){beforeCompleNode.getNext().setBefore(beforeCompleNode.getBefore().getNext());}beforeCompleNode = beforeCompleNode.getNext().getNext();}ListNode fixCompleNode = pHead;ListNode resultHead = null;ListNode resultNode = null;while (fixCompleNode.getNext() != null){if(resultHead == null){resultHead = fixCompleNode.getNext();resultNode = resultHead;}else {resultNode.setNext(fixCompleNode.getNext());resultNode = resultNode.getNext();}fixCompleNode = fixCompleNode.getNext().getNext();}return resultHead;}public static void printListNode(ListNode pHead){ListNode printNode = pHead;while (printNode.getNext() != null){System.out.print("value: " + printNode.getValue());System.out.print(", ");System.out.print("before: " + (printNode.getBefore() != null ? printNode.getBefore().getValue() : ""));System.out.print(", ");System.out.print("next: " + (printNode.getNext() != null ? printNode.getNext().getValue() : ""));printNode = printNode.getNext();System.out.println();}}public static void main(String[] args) {ListNode oldListNode = buildComplexListNode();System.out.println("oldListNode:");printListNode(oldListNode);System.out.println("newListNode:");ListNode mewListNode = complexListNode(oldListNode);printListNode(mewListNode);ListNode newListNode_2 = complexListNode_2(oldListNode);System.out.println("newListNode_2:");printListNode(newListNode_2);}
  • 以上代码中其他未定义方法都沿用 数据结构与算法–链表实现以及应用 中对应的方法。

上一篇:数据结构与算法-- 二叉树中和为某一值的路径
下一篇:数据结构与算法–二叉查找树转顺序排列双向链表

数据结构与算法--复杂链表的复制相关推荐

  1. python定义链表节点_Python数据结构与算法之链表定义与用法实例详解【单链表、循环链表】...

    本文实例讲述了Python数据结构与算法之链表定义与用法.分享给大家供大家参考,具体如下: 本文将为大家讲解: (1)从链表节点的定义开始,以类的方式,面向对象的思想进行链表的设计 (2)链表类插入和 ...

  2. 数据结构与算法之链表结构寻找p、q最近的公共祖先

    链表结构,寻找p.q最近的公共祖先 数据结构与算法之链表结构寻找p.q最近的公共祖先 链表结构,寻找p.q最近的公共祖先 问题 想法 代码 问题 设一棵二叉树的结点结构为(LLINK, INFO, R ...

  3. 数据结构与算法--单链表相关面试题

    此文章仅作为自己学习过程中的记录和总结,同时会有意地去用英文来做笔记,一些术语的英译不太准确,内容如有错漏也请多指教,谢谢! 一.概述 获取单链表的有效元素个数[新浪面试题1] 获取单链表倒数第k个结 ...

  4. 数据结构与算法 内核链表实现商品购物系统项目+Makefile

    数据结构与算法 内核链表实现商品购物系统项目 第一章 项目实现思维 [1]编译介绍 [2]框架思维 第二章 Makefile编写 第三章 代码编写实现 [1]favorite.txt文件 [2]his ...

  5. 一文通数据结构与算法之——链表+常见题型与解题策略+Leetcode经典题

    文章目录 1 链表 1.1 常见题型及解题策略 1.1.1 LeetCode中关于链表的题目有以下五种类型题: 1.1.2 解题策略 1.2 链表的基本内容 1.2.1 链表的基本结构: 1.2.2 ...

  6. 数据结构与算法之-----链表(List)

    [ 写在前面的话:本专栏的主要内容:数据结构与算法. 1.对于​​​​​​​初识数据结构的小伙伴们,鉴于后面的数据结构的构建会使用到专栏前面的内容,包括具体数据结构的应用,所使用到的数据结构,也是自己 ...

  7. Java数据结构和算法(四)--链表

    日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习ArrayList源码的时候就知道了,因为需要把要 插入索引后面的所以元素全部后移一位. 而本文会详细讲解链表,可以解 ...

  8. JS数据结构与算法_链表

    上一篇:JS数据结构与算法_栈&队列 下一篇:JS数据结构与算法_集合&字典 写在前面 说明:JS数据结构与算法 系列文章的代码和示例均可在此找到 上一篇博客发布以后,仅几天的时间竟然 ...

  9. java数据接口之链表_Java数据结构和算法之链表

    三.链表 链结点 在链表中,每个数据项都被包含在'点"中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LI ...

最新文章

  1. [唐胡璐]QTP框架 - 关键字驱动测试框架之七 - Settings管理
  2. JS监听手机物理返回键,返回到指定页面
  3. python while循环语句-Python while循环语句
  4. mysql怎么删除唯一索引_mysql删除唯一索引
  5. 小程序 -- [sitemap 索引情况提示] 根据 sitemap 的规则[0],当前页面 [pages/index/index] 将被索引
  6. jsf集成spring_Spring和JSF集成:导航
  7. LeetCode 1392. 最长快乐前缀(KMP)
  8. RMAN backup recovery area 命令
  9. 波士顿动力的机器狗上班了!挪威石油公司还发其工号
  10. [每日一题] OCP1z0-047 :2013-07-14 正则表达式
  11. 新建一个包,并生成可以直接在命令行执行的指令
  12. alter table *** add constraint *** 用法---约束
  13. DeepLearning tutorial(6)易用的深度学习框架Keras简介
  14. gdb 行号断点调试
  15. reverse函数中的begin和end迭代器
  16. 论神奇宝贝小智精灵联盟名次的类指数型变化
  17. TS中的方法重载,函数重载,构造器重载
  18. 这些数据爬虫网站,帮你工作提质增效,还不收藏?
  19. python做数据分析时缺失值填补、缺失值填充方法汇总
  20. Web开发之常用框架BootStrap

热门文章

  1. svn之迁移代码技巧
  2. 链表之判断一个链表是否为回文结构(一)
  3. tomcat出现5个using_当猫咪出现这5个迹象,主人就要给猫咪换猫粮了
  4. 双时隙的工作原理_OFDM调制技术原理是什么 OFDM调制实现原理介绍【图文】
  5. 服务器安全维护包含,服务器安全维护包含
  6. Sigmoid函数与逻辑回归
  7. 博古通今的孩子是怎么养成的?答案就在这本影响了无数中国人思想的奇书里……
  8. 那些拧不开瓶盖的女生全都是装的?理工男这样想......
  9. 史上最惨锦鲤即将来袭!奖品堪比5年高考3年模拟!
  10. 史上首次!世界杯使用视频裁判