(定义一个新的数据结构,每个节点除了具有普通链表的next域外,还有一个额外的引用指向任意节点。我们要对由该特殊数据结构形成的链表进行复制。)

我的方法:也就是克隆一个这种特殊链表,很快想到先不考虑原链表sibling域,复制出一个新的链表,然后再去给sibling域赋值。由于sibling可以指向任何节点,而且我们是根据原链表的sibling来确定新链表中的sibling,所以每次我们寻找新链表中某个节点的sibling,都要两个指针重新从头开始扫描以确定新链表中的sibling。所以时间复杂度是O(n)+O(n^2)。

    	public ComplexListNode complexClone(ComplexListNode head){
if(head == null)return null;
ComplexListNode headMark = head;
ComplexListNode newHeadMark = new ComplexListNode(head.val);
ComplexListNode newHead = newHeadMark;
head = head.next; //仅形成next域
for(; head != null; head = head.next){
newHead.next = new ComplexListNode(head.val);
newHead = newHead.next;
}
//形成sibling域
head = headMark;
newHead = newHeadMark;
for(; head!=null; head=head.next, newHead=newHead.next){
ComplexListNode index = headMark;
ComplexListNode newIndex = newHeadMark;
for(; index!=null; index=index.next,newIndex=newIndex.next){
if(head.sibling == index){
newHead.sibling = newIndex;
}
}
}
return newHeadMark;
}

书中方法一:一看书,果然上面的方法是最烂的,主要时间复杂度集中在第二步确定sibling上面,我们是否能够让每次寻找的时间复杂度减小到O(1)?一般来说牺牲空间复杂度可以降低时间复杂度+一次寻找的时间复杂度是O(1),我们就想到了HashMap。如何使用呢?我们在创建新链表的时候存储原链表每个节点对应的新链表节点,如(old,new),这样在第二步连接sibling的时候就可以根据原链表节点一步找到新链表节点。

    	public ComplexListNode complexClone2(ComplexListNode head){
if(head == null)return null;
Map<ComplexListNode, ComplexListNode> map = new HashMap<>();
ComplexListNode headMark = head;
ComplexListNode newHeadMark = new ComplexListNode(head.val);
ComplexListNode newHead = newHeadMark;
map.put(headMark, newHead); head = head.next;
for(; head!=null; head = head.next){
newHead.next = new ComplexListNode(head.val);
map.put(head, newHead.next);
newHead = newHead.next;
}
for(ComplexListNode index = headMark,newIndex = newHeadMark;
index!=null; index=index.next, newIndex=newIndex.next){
newIndex.sibling = map.get(index.sibling);
}
return newHeadMark;
}

书中方法二:书上还讲了一种时间复杂度O(n)空间复杂度O(1)的方法。我们既可以根据原链表的节点迅速确定新链表的节点(即和HashMap一样也存在一种一一对应的关系),又不用额外创建空间。创建新链表时,把新链表的对应节点放在原链表节点的后面可以达到一一对应的效果,最后我们把一整条链表拆开,这样也不会破坏源链表结构,也得到了新链表。从本质上来讲,如果我们完全抛弃原链表的结构去寻找sibling,相当于丢弃了一部分信息,也就是把所有节点当成一个set去遍历寻找。如果考虑了sibling的结构(即把新的节点创建在原节点之后),相当于走了捷径。

    	public ComplexListNode complexClone3(ComplexListNode head){
copyAndConstruct(head);
linkSibling(head);
return unpackage(head);
}
private void copyAndConstruct(ComplexListNode head){
ComplexListNode index = head;
while(index != null){
ComplexListNode temp = new ComplexListNode(index.val);
temp.next = index.next;
index.next = temp;
index = index.next.next;
}
}
private void linkSibling(ComplexListNode head){
ComplexListNode index = head;
while(index != null){
if(index.sibling == null){
index.next.sibling = null;
}else{
index.next.sibling = index.sibling.next;
}
index = index.next.next;
}
} private ComplexListNode unpackage(ComplexListNode head){
if(head == null)return null;
ComplexListNode newIndex = head.next;
ComplexListNode newHead = newIndex;
ComplexListNode index = head;
while(index != null){
index.next = newIndex.next;
if(newIndex.next != null){
newIndex.next = newIndex.next.next;
}
index = index.next;
newIndex = newIndex.next;
}
return newHead;
}

《剑指offer》面试题26 复杂链表的复制 Java版的更多相关文章

  1. 剑指offer 面试题35.复杂链表的复制

    时间O(N),空间O(N) /* struct RandomListNode { int label; struct RandomListNode *next, *random; RandomList ...

  2. 剑指offer面试题26-复杂链表的复制

    题目: 请实现函数ComplexListNode* Clone(ComplexListNode* pHead).复制一个复杂链表. 在复杂链表中.每个节点除了一个m_pNext指针指向下一个节点外,另 ...

  3. 剑指Offer - 九度1524 - 复杂链表的复制

    剑指Offer - 九度1524 - 复杂链表的复制2014-02-07 01:30 题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点 ...

  4. 剑指Offer面试题:14.链表的倒数第k个节点

    PS:这是一道出境率极高的题目,记得去年参加校园招聘时我看到了3次,但是每次写的都不完善. 一.题目:链表的倒数第k个节点 题目:输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题 ...

  5. 剑指offer——27. 二叉搜索树与双向链表(Java版)

    题目: 剑指offer的题目有挺多都挺典型的,就像这一道.不过书中的代码写的真是ugly,有很多题目LeetCode上都有,可以去LeetCode讨论区看看,经常有一些大神分享,写的代码真是高效.简洁 ...

  6. 剑指offer面试题26:复杂链表的复制

    题目:请实现一个函数,复制一个复杂链表. 在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个sibling指针指向链表中的任意结点或者nulL 直观解法: 1.遍历链表,复制链表节 ...

  7. 剑指Offer:面试题26——复制复杂的链表(java实现)

    问题描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点). 思路1: 1.先复制链表节点,并用next链接起来. 2.然后对每一个结点去修改 ...

  8. 剑指Offer:面试题16——反转链表(java实现)

    问题描述 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后的链表的头结点.链表结点如下: public class ListNode { int val; ListNode next = n ...

  9. 【剑指offer 面试题15】链表中倒数第K个结点

    思路: 定义两个指针同时指向head,第一个指针先走K-1步,随后二个指针同时移动,当第一个指针到末尾处时,第二个指针所指向的即为倒数第K个结点. #include <iostream> ...

随机推荐

  1. 原生js数组排序(封装方法)

    //两值互换 function Sort(arr, index){ //参数arr代表数组,index代表数组元素下标 arr[index] += arr[index + 1];  //a+=b; a ...

  2. select编程[回调+事件循环]

    感觉比java写起来还要忧伤..... """ select , poll,epoll 注意: epoll并不一定比select的性能好,这需要看场景 1. 在高并发场景 ...

  3. 8.django单独执行文件

    import os import django # 导入django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'noju.settings')d ...

  4. Mysql包的下载

    官方下载地址: https://dev.mysql.com/downloads/mysql/5.5.html#downloads mysql的下载界面 二进制的包 通用的RPM包 源码包

  5. 3D Computer Grapihcs Using OpenGL - 17 添加相机(旋转)

    在11节我们说过,MVP矩阵中目前只应用了两个矩阵,World to View 矩阵被省略了,这就导致我们的画面没有办法转换视角. 本节我们将添加这一环节,让相机可以旋转. 为了实现这一目的,我们添加 ...

  6. 【错误记录】Python 负数取余问题

    print(-123%10)   # 输出 7   print(-123%-10)  # 输出 -3 这里面第二条是我们一般意义上的取余操作.这里也特别标注一下,如果涉及到负数取余要用上述解决办法. ...

  7. 聊聊spring-boot-starter-data-redis的配置变更

    本文主要研究一下spring-boot-starter-data-redis的配置变更 配置变更 以前是spring-boot的1.4.x版本的(spring-data-redis为1.7.x版本), ...

  8. mysql 安装教程(详细说明)

    如果你装过,一定要先卸载干净,并且重启重新装.卸载教程(保证成功)https://www.cnblogs.com/qzhc/p/11354678.html 大家都知道MySQL是一款中.小型关系型数据 ...

  9. mysql数据库修改一行数据格式不成功问题

    举个例子: mysql数据库中有两个字段publication_time.storage_time,我尝试着一个一个的修改字段的状态 #alter table books modify column ...

  10. C语言字符数组详解

    字符串的存储方式有字符数组和字符指针,我们先来看看字符数组. 因为字符串是由多个字符组成的序列,所以要想存储一个字符串,可以先把它拆成一个个字符,然后分别对这些字符进行存储,即通过字符数组存储.字符数 ...