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

Posted jefferychenxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指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,并作了一些比较基本的处理。
技术图片
这里展示一轮循环的结果
技术图片

以上是关于剑指Offer对答如流系列 - 复杂链表的复制的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer对答如流系列 - 在O时间删除链表结点

剑指offer系列——25.复杂链表的复制

剑指Offer对答如流系列 - 链表中倒数第k个结点

剑指Offer对答如流系列 - 链表中环的入口节点

剑指Offer对答如流系列 - 合并两个排序的链表

剑指offer--35复杂链表的复制