剑指Offer对答如流系列 - 复杂链表的复制

面试题35:复杂链表的复制

题目描述

请实现函数ComplexListNode Clone(ComplexListNode pHead),复制一个复杂链表。在复杂链表中,每个节点除了有一个next引用向下一个节点外,还有一个sibling 指向链表中的任意节点或者null。

节点的定义如下:

    public class ComplexListNode {
        int val;
        ComplexListNode next = null;
        ComplexListNode sibling = null;

        ComplexListNode(int label) {
            this.val = label;
        }
    }

问题分析

单纯只完成复制功能的话,还是比较容易想到的。

1.首先先复制节点,用next链接,之后根据原始节点的sibling引用确定该sibling节点距离头节点的位置,从而对复制节点设置sibling引用。但是该思路对于n个节点的链表,每个节点的sibling都需要O(n)个时间才能找到,所以时间复杂度为O(n^2)

因为我们无法直接定位sibling引用的节点的位置(而且无法改变链表节点的定义),这个时候为了达到时间复杂度较低的目的应该要想到拿空间换时间的做法。既然涉及到查询,最容易想到的是使用哈希表。

2.我们可以复制原始节点N创建N’,用next链接。将<N,N‘>的配对信息存放入一个哈希表中;在设置sibling时,通过哈希表,只需要用O(1)的时间即可找到复制节点的sibling。该方法的时间复杂度为O(n),但空间复杂度为O(n)。

当然哈希表并不是唯一的解决思路

3.我们可以复制原始N创建N’,将N‘链接到N的后面;根据原始节点N的sibling可以快速设置N‘节点的sibling,最后将这个长链表拆分成原始链表和复制链表(根据奇偶位置)

画图是比较容易找出这种方法的。

问题解决

  // 主程序
    public ComplexListNode cloneList(ComplexListNode head) {
        // 1.复制节点
        cloneNodes(head);
        // 2.设置sibling
        connectSiblingNodes(head);
        // 3.拆分长链表
        return reconnectNodes(head);
    }

    // 第一步:复制每个节点,并插入到原始节点的后面
    private void cloneNodes(ComplexListNode head) {
        ComplexListNode pNode=head;
        while(pNode!=null) {
            ComplexListNode clonedNode=new ComplexListNode(pNode.val);
            clonedNode.next=pNode.next;
            pNode.next=clonedNode;
            pNode=clonedNode.next;
        }
    }

    // 第二步:根据原节点的sibling,设置复制节点的sibling
    private void connectSiblingNodes(ComplexListNode head) {
        ComplexListNode pNode=head;
        while(pNode!=null) {
            // siblingNode可能为null
            if(pNode.sibling!=null) {
                pNode.next.sibling=pNode.sibling.next;
            }
            pNode=pNode.next.next;
        }
    }

    // 第三步:将长链表拆分成原始链表和复制链表(根据奇偶位置)
    private ComplexListNode reconnectNodes(ComplexListNode head) {

        ComplexListNode clonedHead=null;
        ComplexListNode clonedNode=null;
        ComplexListNode pNode=head;

        if(head!=null) {
            clonedHead=pNode.next;
            clonedNode=pNode.next;
            pNode.next=clonedNode.next;
            // 提前将pNode指向下一个节点,方便判断是否为null
            pNode=pNode.next;
        }
        while(pNode!=null) {
            clonedNode.next=pNode.next;
            clonedNode=clonedNode.next;
            pNode.next=clonedNode.next;
            pNode=pNode.next;
        }
        return clonedHead;
    }

有读者说将长链表拆分成原始链表和复制链表 看不明白,我想说这种抽象的题,刚开始从来都不是看明白的,而是画明白的。

第一件事是判断头节点是否为null,并作了一些比较基本的处理。

这里展示一轮循环的结果

原文地址:https://www.cnblogs.com/JefferyChenXiao/p/12246412.html

时间: 2024-08-29 10:48:01

剑指Offer对答如流系列 - 复杂链表的复制的相关文章

剑指Offer对答如流系列 - 反转链表

面试题24:反转链表 题目描述 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点 链表结构 public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } 问题分析 头插法是反转链表非常经典的一种手段,这里演示一下吧,毕竟这个在JDK源码中也能遇见. 核心代码如下: ListNode Inverse(ListNode L) { ListNode p,

剑指Offer对答如流系列 - 从上往下打印二叉树

面试题32:从上往下打印二叉树 题目描述 树的结构定义如下: public class Node{ int e; Node left; Node right; Node(int x) { e = x; } } (一)不分行从上到下打印二叉树 从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印. 比如下面二叉树,输出顺序为 8 6 10 5 7 9 11 (二)分行从上到下打印二叉树 从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行. 比如下面二叉树,输出

剑指Offer对答如流系列 - 礼物的最大价值

面试题47:礼物的最大价值 题目描述 在一个m×n的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0).你可以从棋盘的左上角开始拿格子里的礼物,并每次向左或者向下移动一格直到到达棋盘的右下角.给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物? 比如下面的棋盘中,如果按照红色数字的路线走可以拿到最大价值为53的礼物 问题分析 动态规划:定义f(i,j)为到达(i,j)位置格子时能拿到的礼物总和的最大值,则有:f(i,j)=max{f(i-1,j),f(i,j-1)}+va

剑指Offer对答如流系列 - 序列化二叉树

面试题37:序列化二叉树 题目描述 请实现两个函数,分别用来序列化和反序列化二叉树. 树的结构定义如下: public class Node { int val = 0; Node left = null; Node right = null; public Node(int val) { this.val = val; } } 问题分析 一般情况下,需要采用前/后序遍历和中序遍历才能确定一个二叉树,具体的内容我们之前探讨过 剑指Offer对答如流系列 - 重建二叉树 但是采用这种方式进行序列化

剑指Offer对答如流系列 - 把数组排成最小的数

面试题45:把数组排成最小的数 题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3, 32, 321},则打印出这3个数字能排成的最小数字321323. 问题分析 之前我们做过字符全排列的习题 剑指Offer对答如流系列 - 字符串的排列,但是将算法思想应用到这一题的话,效果不好,求出所有的组合,再计算出组合的最小值,这效率该多低啊. 我们还要进一步探究,看看有没有不错的规律,供我们使用. 因为数字拼接后的长度一样,拼接后的结果

剑指Offer对答如流系列 - 数组中数字出现的次数

面试题56:数组中数字出现的次数 题目描述 问题(1)数组中只出现一次的两个数字 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 问题(2)数组中唯一只出现一次的数字 在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次.请找出那个只出现一次的数字. 问题分析 问题(1)分析 在这篇文章剑指Offer对答如流系列 - 二进制中 1 的个数中,我们详细探讨了位运算,其中有重要的一条:两个相同的数异

剑指Offer对答如流系列 - 求1+2+…+n

面试题64:求1+2+-+n 题目描述 求1+2+-+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 问题分析 有了那么多限制,剩下的我们可以选择 单目运算符:++和--,双目运算符:+,-,移位运算符<>,关系运算符>,<等 逻辑运算符&&,||,&,|,^,赋值= 既然是一个等差数列,和为(n+1)*n/2 我们之前详细探讨了位运算剑指Offer对答如流系列 - 二进制中 1 的个

剑指Offer对答如流系列 - 不用加减乘除做加法

面试题65:不用加减乘除做加法 题目描述 写一个函数,求两个整数之和,要求在函数体内不得使用+.-.×.÷四则运算符号. 问题分析 我们之前详细探讨了位运算 剑指Offer对答如流系列 - 二进制中 1 的个数,已经非常非常详细了. 这道题仅仅是让做加法,我们除此之外还是做了乘除与减法. 记不清的朋友可以回头看看. 这里象征性地做一次解答吧 问题解答 public int add(int num1,int num2) { while(num2!=0){ int sum=num1^num2; in

剑指Offer对答如流系列 - 从1到n整数中1出现的次数

面试题43:从1到n整数中1出现的次数 题目描述 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数. 例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次. 问题分析 最容易想到的思路是通过对10求余数判断整数的个位数字是不是1.代码书写也很简单,但是如果输入的整数n比较大的时候,会有大量的运算. 1是由于数字递增出现的,而十进制影响这种出现的周期性.这本身肯定存在规律,重要的是耐心寻找,不要妄想一次性就找出来,下面的规律要比<剑指Offer>