• 剑指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,并作了一些比较基本的处理。
    在这里插入图片描述
    这里展示一轮循环的结果
    在这里插入图片描述

  • 相关阅读:
    frp 多个web服务内网映射外网IP
    vue 组件和路由 cdn引入方式的写法。。。。
    Http/Https面试题整理+三次握手四次挥手
    推荐系统的工程实现
    Testner读书会暨公益图书馆项目正式启动
    学习算法
    工作流开源任务调度框架2--Azkaban
    工作流开源调度框架1- airflow
    Testner软件质量与测试创新研究中心总部落地长沙
    企业的八大类别36种盈利模式
  • 原文地址:https://www.cnblogs.com/JefferyChenXiao/p/12246412.html
Copyright © 2020-2023  润新知