• [剑指offer] 复杂链表的复制


    题目

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

    示例 1:

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

    输入:head = [[1,1],[2,1]]
    输出:[[1,1],[2,1]]
    示例 3:

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

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

    提示:

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

    分析(主要参考Krahets的题解

    我们复制一般的链表是这么做的

    class Solution {
        public Node copyRandomList(Node head) {
            Node cur = head;
            Node dum = new Node(0), pre = dum;
            while(cur != null) {
                Node node = new Node(cur.val); // 复制节点 cur
                pre.next = node;               // 新链表的 前驱节点 -> 当前节点
                // pre.random = "???";         // 新链表的 「 前驱节点 -> 当前节点 」 无法确定
                cur = cur.next;                // 遍历下一节点
                pre = node;                    // 保存当前新节点
            }
            return dum.next;
        }
    }
    

    出现的问题(即注释掉的语句)主要是,我们的链表节点是一个一个建立起来的,如图

    我们建立哨兵节点0后,建立第一个节点7,但是7后面的元素都还没构造出来,所以cur.random实际上是节点7调用Node构造函数的时候默认初始化的null,故输出凡是涉及到random,全是null。

    题解1:使用哈希表

    先用map建立和原复杂链表一样的链表,遍历新的链表(前后节点都已经建立好了),明确random的指向

    // Definition for a Node.
    class Node {
        int val;
        Node next;
        Node random;
    
        public Node(int val) {
            this.val = val;
            this.next = null;
            this.random = null;
        }
    }
    
    class Solution {
        public Node copyRandomList(Node head) {
            if(head == null) return null;
            //从头遍历,将
            Node cur = head;
            Map<Node, Node> hMap= new HashMap<>();
            //复制各节点,建立原节点->新节点的map映射
            while(cur != null){
                hMap.put(cur, new Node(cur.val));
                cur = cur.next;
            }
            //将cur重新放回头部
            cur = head;
            //构建新链表的next和random指向
            while(cur != null){
                hMap.get(cur).next = hMap.get(cur.next);
                hMap.get(cur).random = hMap.get(cur.random);
                cur = cur.next;
            }
            //返回新链表的头结点
            return hMap.get(head);
        }
    }
    

    题解2 拼接+拆分

    将新节点拼接在老节点后(eg.1->2->3 变为1->new_1->2-> new_2->3->new_3),明确random指向,返回拆分后的链表

    class Solution {
        public Node copyRandomList(Node head) {
            if(head == null) return null;
            //从头遍历,将
            Node cur = head;
            //1.复制节点,构建拼接链表
            while(cur != null){
                Node tmp = new Node(cur.val);
                tmp.next = cur.next;
                cur.next = tmp;
                cur = tmp.next;
            }
            //2.构建各节点的random指向
            cur = head;
            while(cur != null){
                if(cur.random != null)//防止出现null.next抛出空指针异常,为空的情况不用特殊处理,因为新节点构造时默认初始化的random已经是null了。
                    cur.next.random = cur.random.next;//cur.random非空的情况,新节点的random应该等于原来random的下一位
                cur = cur.next.next;//cur移动两位,指向原链表节点的下一位。
            }
            //3.拆分两链表
            cur = head.next;
            Node pre = head, res = head.next;
            while(cur.next != null){
                pre.next = pre.next.next;
                cur.next = cur.next.next;
                pre = pre.next;
                cur = cur.next;
            }
            pre.next = null;//单独处理原链表表尾节点
            return res;//返回新链表头节点
        }
    }
    
    日积月累,水滴石穿
  • 相关阅读:
    【2020-05-17】人生十三信条
    【2020-05-16】评价与骄傲
    【2020-05-15】每天都充满向上的激情
    【04NOIP普及组】火星人(信息学奥赛一本通 1929)(洛谷 1088)
    next_permutation(全排列算法)
    【03NOIP普及组】麦森数(信息学奥赛一本通 1925)(洛谷 1045)
    快速幂
    【03NOIP普及组】栈(信息学奥赛一本通 1924)(洛谷 1044)
    【06NOIP普及组】数列(信息学奥赛一本通 1937)(洛谷 1062)
    【00NOIP普及组】计算器的改良(信息学奥赛一本通 1910)(洛谷 1022)
  • 原文地址:https://www.cnblogs.com/lonelyisland/p/14451710.html
Copyright © 2020-2023  润新知