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

复杂链表的复制

  • 题目:实现一个函数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 …

我是如何一步步的在并行编程中将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;面试的…

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

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

使用 docker 编译运行 abp 项目

在前面的两篇文章中&#xff0c;介绍了如何在华为鲲鹏架构及其Euler系统上运行dotnet core, 使用docker运行了默认的mvc模板项目&#xff0c;这篇文章继续介绍在docker中运行更复杂的dotnet core项目&#xff0c;这里以业内鼎鼎大名的abp vnext框架&#xff0c;版本 2.6 为例。…

数据结构与算法--数组中的逆序对

题目&#xff1a;在数组中的两个数字如果签名一个数字大于后面的数组&#xff0c;则这两个数字组成一个逆序对。输入一个数组&#xff0c;求出这个数组中的逆序对的总数。 案例&#xff1a;输入数组{7,5&#xff0c;6,4}中一共有5个逆序对分别是{7,6}&#xff0c;{7,5}&#x…

用了这么多年的泛型,你对它到底有多了解?

现代程序员写代码没有人敢说自己没用过泛型&#xff0c;这个泛型模板T可以被任何你想要的类型替代&#xff0c;确实很魔法很神奇&#xff0c;很多人也习以为常了&#xff0c;但就是这么有趣的泛型T底层到底是怎么帮你实现的&#xff0c;不知道有多少人清楚底层玩法&#xff0c;…