zoukankan      html  css  js  c++  java
  • Medium | 剑指 Offer 35. 复杂链表的复制

    剑指 Offer 35. 复杂链表的复制

    请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

    示例 1:

    img
    输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
    输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
    

    示例 2:

    img
    输入:head = [[1,1],[2,1]]
    输出:[[1,1],[2,1]]
    

    示例 3:

    img

    输入:head = [[3,null],[3,0],[3,null]]
    输出:[[3,null],[3,0],[3,null]]
    

    示例 4:

    输入:head = []
    输出:[]
    解释:给定的链表为空(空指针),因此返回 null。
    

    提示:

    • -10000 <= Node.val <= 10000
    • Node.random 为空(null)或指向链表中的节点。
    • 节点数目不超过 1000 。

    方法一:HashMap

    一个简单的想法就是分为两步走, 先把next指针全部指向相应的克隆的节点, 然后再处理random指针

    遍历原链表节点并赋值节点, 将前驱节点的指针修改为克隆的节点。这样一趟遍历完成后, 所有next指针全部指向正确的位置了。那么random指针的指向问题如何处理呢?

    1. 简单来想, 我可以设置一个计数器只是某个链表的节点是第几个节点。这样我依据可以通过遍历链表的方式找到原链表节点random指针指向链表的第一个元素。然后依据此值,遍历克隆链表, 找到这个节点, 并相应的给克隆节点的random指针赋值即可。

    2. 上述的思路其实非常繁琐, 每找一个random都要遍历一遍原链表和克隆链表, N个节点要遍历N次。就是为了找节点之间的对应关系, 面对这样的问题, 我们只需要在第一次克隆链表, 赋值Next指针的过程中, 把原节点和克隆节点的对应关系保存到HaspMap里, 就能通过原节点的Random指针, 迅速找到可能节点的Random指向元素的指针了。比如对应关系分别是(A1,A2), (B1,B2), (C1,C2)... (Z1,Z2)。如果A1的random指针指向Z1, 那么我立马就可以根据节点的对应关系, 让克隆节点A2的random指针指向Z2, 这样random指针的复制也只需要遍历一遍数据就可以全部完成了。

    方法二

    其实方法一的时间复杂度已经优化不错了, 但是空间复杂度还有可优化的空间。

    这种方法把步骤分为3步:

    1. 复制节点, 并且将复制的节点放在原节点的后面, 先用next指针串起来

    2. 遍历链表, 赋值random指针。因为克隆节点的random指向的节点可以通过原节点的random指针的next指针直接得到。所以这一步的可以直接赋值。

    3. 然后将链表的Next指针还原, 并拆分成两个链表

    其实这种方法的核心思想是和第一种HashMap的思想是一致的, 那就是保存原节点和克隆节点之间的对应关系, 方法一使用额外的HashMap来保存对应关系, 而这种方法,使用next指针保存对应关系。

    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        cloneNode(head);
        connectSibling(head);
        return splitLinkedList(head);
    }
    // 克隆链表, 并将克隆的节点, 修改原节点的next指向克隆节点
    public void cloneNode(Node head) {
        Node p = head;
        while(p != null) {
            Node pClone = new Node(p.val);
            Node nextNode = p.next;
            p.next = pClone;
            pClone.next = nextNode;
            p = nextNode;
        }
    }
    // 连接random指针
    public void connectSibling(Node head) {
        Node p = head;
        while(p != null) {
            // 这里要注意random指针可能为空, 要额外判断
            p.next.random = ((p.random != null) ? p.random.next : null);
            p = p.next.next;
        }
    }
    // 将链表还原回来 next指针重新指向下一个节点
    public Node splitLinkedList(Node head) {
        Node headClone = head.next;
        Node p = head;
        while(p != null) {
            Node nextNode = p.next.next;
            Node pClone = p.next;
            p.next = nextNode;
            pClone.next = nextNode != null ? nextNode.next : null;
            p = nextNode;
        }
        return headClone;
    }
    
  • 相关阅读:
    【WIN32API&DAPI】窗口相关函数
    第十四章_安全性
    android实现gif图与文字混排
    Extjs 4.2 设置buttontext为中文
    HDU 5384 Danganronpa (AC自己主动机模板题)
    bzoj2938【Poi2000】病毒
    [Java开发之路](9)对象序列化与反序列化
    atitit.jndi的架构与原理以及资源配置and单元測试实践
    QueryError:Incorrect result size: expected 1, actual 0
    LightOJ 1070 Algebraic Problem (推导+矩阵高速幂)
  • 原文地址:https://www.cnblogs.com/chenrj97/p/14281646.html
Copyright © 2011-2022 走看看