数据结构与算法--复杂链表的复制

复杂链表的复制

  • 题目:实现一个函数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);}
  • 以上代码中其他未定义方法都沿用 数据结构与算法–链表实现以及应用 中对应的方法。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310382.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何实时主动监控你的网站接口是否挂掉并及时报警

“ 阅读本文大概需要 10 分钟。 ”最近我在公司负责的业务已经正式投入上线了&#xff0c;既然是线上环境&#xff0c;那么就需要保证其可用性。我负责的业务其中就包括一个 Web Service&#xff0c;我需要保证 Service 的每个接口都是可用的&#xff0c;如果某个时间流量大了或…

数据结构与算法--二叉查找树转顺序排列双向链表

二叉查找树转顺序排列双向链表 题目&#xff1a;输入一颗二叉查找树&#xff0c;将二叉查找树转成一个排序的双向链表&#xff0c;要求不能创建任何新节点&#xff0c;只调整树节点中指针的指向。例如下图所示&#xff1a; 本次二叉查找树节点定义使用之前文章 数据结构与算法…

5种避免C#.NET中因事件造成内存泄漏的技术

原文来自互联网&#xff0c;由长沙DotNET技术社区编译。 5种避免C&#xff03;.NET中事件造成的内存泄漏的技术C&#xff03;&#xff08;通常是.NET&#xff09;中的事件注册是内存泄漏的最常见原因。至少从我的经验来看。实际上&#xff0c;我从事件中看到了太多的内存泄漏&a…

数据结构与算法--字符串的排列组合问题

字符串的全排列 题目&#xff1a;输入一个字符串&#xff0c;打印出改字符串中所有字符的所有排列。例如输入字符串abc&#xff0c;那么打印出由a&#xff0c;b&#xff0c;c字符组成的所有字符串&#xff1a;abc&#xff0c;acb&#xff0c;bac&#xff0c;bca&#xff0c;cab…

[GitHub] 75+的 C# 数据结构和算法实现

C#中标准数据结构和算法的即插即用类库项目GitHub&#xff1a;https://github.com/aalhour/C-Sharp-AlgorithmsWatch: 307 Star: 3.4k Fork: 910o---o | |/ --O---O--O | |\ --O---O--o---o | |O o o--o o--o o---o o-O-o …

C++实现井字棋小游戏(写得不好,留作纪念!!!)

回宿舍路上&#xff0c;同学问起我能不能用C写个五子棋游戏&#xff0c;我说应该挺简单的&#xff0c;但是我不会写&#xff0c;然后他说不用写五子棋&#xff0c;就写井字棋吧&#xff01;&#xff01;&#xff01;我说试试吧&#xff01;&#xff01;&#xff01; (不过说实话…

数据结构与算法-- 数组中出现次数超过一半的数字(时间复杂度的讨论)

时间效率 互联网想对时间效率格外的敏感&#xff0c;所以我们总是在需求迭代一定程度后去做优化。而且我们解决问题的时候&#xff0c;时间效率往往是一个考查的重点。因此我们平时编码过程中就必须不断的优化效率&#xff0c;追求完美的态度与能力。 首先是编码习惯&#xff…

我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程

在并行编程中&#xff0c;经常会遇到多线程间操作共享集合的问题&#xff0c;很多时候大家都很难逃避这个问题做到一种无锁编程状态&#xff0c;你也知道一旦给共享集合套上lock之后&#xff0c;并发和伸缩能力往往会造成很大影响&#xff0c;这篇就来谈谈如何尽可能的减少lock…

常用Arthas命令

jad反编译 检查线上代码是否修改成功&#xff0c;例如修改interface后看Jar包是否引入新的&#xff0c;或者代码是否最新的。 jad com.zhenai.counseling.business.provider.facade.supremecourse.RedeemRecordFacadeImpl //反编译只展示源码 jad --source-only com.zhenai.c…

关于分布式锁的面试题都在这里了

「我今天班儿都没上&#xff0c;就为了赶紧把这篇文章分布式锁早点写完。我真的不能再贴心了。」「边喝茶边构思&#xff0c;你们可不要白嫖了&#xff01;三连来一遍&#xff1f;」引言为什么要学习分布式锁&#xff1f;最简单的理由就是作为一个社招程序员&#xff0c;面试的…

数据结构与算法--将数组排成最小的数

将数组排成最小的数 题目&#xff1a;输入一个正整数的数组&#xff0c;将数组中所有数字拼接在一起排列成一个新的数&#xff0c;打印能拼接出来的所有数字中最小的一个&#xff0c; 案例&#xff1a;输入数组{12,4&#xff0c;55}&#xff0c;则能打印出最小的数组是&#x…

Git 15周年:当年的分道扬镳,成就了今天的开源传奇

4 月 7 日&#xff0c;全球最主流的版本控制系统 —— Git 迎来 15 周年纪念日&#xff0c;项目主要维护者 Junio C Hamano&#xff08;滨野 纯&#xff09; 先生发邮件庆祝了这一日子。我们知道&#xff0c;所有的软件项目在整个生命周期中都要经过不断迭代&#xff0c;在一个…

数据结构与算法--丑数

找出排在第n位大的丑数 丑数&#xff1a;我们将只包含质因子 2,3,5的数称为丑数&#xff08;ugly Number&#xff09;。求按从小到大的熟悉怒排列的低1500 个丑数。例如6,8 都是丑数&#xff0c;但是14 不是丑数&#xff0c;因为他包含质因子7。1 是基础丑数 解法一&#xff1…

数据结构与算法--第一个只出现一次的字符

第一个只出现一次的字符 题目&#xff1a;在字符串中找出第一个只出现一次的字符&#xff0c;比如输入“wersdfxvsdfwer”&#xff0c;则输出x。 方法一&#xff1a; 还是老规矩&#xff0c;初始想法是从头遍历每一个字符&#xff0c;每遍历一个字符都与后面n-1个字符比较如果…