复制带有随机指针节点的链表

复制带有随机指针节点的链表

问题重述:

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点

例如,如果原链表中有 XY 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 xy ,同样有 x.random --> y

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0n-1);如果不指向任何节点,则为 null

你的代码 接受原链表的头节点 head 作为传入参数。

示例 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]]

提示:

  • 0 <= n <= 1000
  • -104 <= Node.val <= 104
  • Node.randomnull 或指向链表中的节点。

问题分析:

题目要求我们复制一个新的链表,链表的每一个结点和原链表都一样,如果没有随机指针,那么我们直接复制结点就可以,但是这道题每个节点都有随机指针,而且新复制的结点随机指针对应的结点也是新复制的结点,所以这道问题就转换成了如何将复制节点的random指向对应的复制结点,由于复制节点的random指向的结点和原结点一样,那么我们可以使得原结点和复制节点一一映射,然后根据原结点的random确定复制节点的random(这就需要使用哈希表,一一映射)

还有一种方法,可以降低空间复杂度,那就是把复制节点直接连接在原结点后,然后复制节点的random可以直接等于原结点的random的下一个结点,这种方法只需要注意random可能为null即可

解法:

哈希表

解题:

代码:
常规:

时间复杂度:O(N),空间复杂度O(N)

public Node copyRandomList(Node head) {
        if(head == null){
            return head;
        }
        // 创建哈希表对象,并且创建一个结点用于记录头节点
        HashMap<Node,Node> map = new HashMap();
        Node cur = head;
        // 循环链表,将链表元素加入到哈希表中
        while(cur != null){
            map.put(cur,new Node(cur.val));
            cur = cur.next;
        }
        // 循环完成后,哈希表内键为原始结点,值为副本节点,指向全部为空
        // 接下来遍历哈希表
        cur = head;
        while(cur != null){
            // 现在的cur是哈希表内的键,我们可以根据这个值来操作哈希表
            Node node = map.get(cur);
            node.next = map.get(cur.next);
            node.random = map.get(cur.random);
            cur = cur.next;
        }
        return map.get(head);
    }

代码解析:创建一个哈希表,哈希表的键和值的范围都是Node,因为都是用来存放链表结点的,因为我们是要对复制节点进行操作,因此我们原结点作为键,复制节点作为键,第一次循环,将源节点和复制节点添加到哈希表中,然后对链表进行第二次遍历,遍历过程中可以通过当前节点作为键找到复制节点,通过当前节点的random去确定复制节点的random。

进阶

时间复杂度O(N),空间复杂度O(1)

public Node copyRandomList(Node head) {
        if(head == null){
            return head;
        }
        // 复制结点
        Node cur = head;
        Node next = null;
        while(cur != null){
            // 循环链表,将每个节点复制一个新的放到自己的后面
            next = cur.next;
            // 创建一个副本结点,将该节点连接到当前节点和下一个节点中间
            cur.next = new Node(cur.val);
            cur.next.next = next;
            cur = next;
        }
        // 循环结束后,链表是原结点和副本节点交替连接起来
        // 再次循环链表,将副本节点的random联结起来
        cur = head;
        Node copyNode = null;
        while(cur != null){
            copyNode = cur.next;
            // 这一步是将副本节点的random连接到对应的节点上去,需要注意节点的random可能为null
            next = (cur.random == null ? null : cur.random.next);
            copyNode.random = next;
            // 更新cur
            cur = copyNode.next;
        }
        // 此次循环结束后,所有副本节点的random都已经连接完毕,接下来就是将原链表和副本节点链表拆分开来
        cur = head;
        Node resHead = head.next;
        while(cur != null){
            copyNode = cur.next;
            // 因为复制节点的下一个结点可能为null
            next = (copyNode.next == null ? null : copyNode.next);
            cur.next = next;
            copyNode.next = (next == null ? null : next.next);
            cur = next;
        }
        return resHead;
    }

总结:

当遇见的问题需要一一映射的时候,我们就可以使用哈希表,一般数组用来排序或者进行数值的比较

上一篇:计算机视觉中的图像标注工具总结


下一篇:Win10安装labelImage