138. 复制带随机指针的链表

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

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

我的思路:

random指针会出现两种情况:

1.指向的节点还没创建

2.指向的节点创建过了

建个map保存下来之前遍历过的原链表节点,当新链表的节点需要random指向之前的节点时使用

由于本题在复制的过程中存在random需要指向的节点不存在,所以需要额外再用个map保存一下

等待新链表创建了之后再找到之前的节点指向

代码如下:

public Node copyRandomList(Node head) {
    Map<Node,Node> map = new HashMap<>();//不用链表是因为想创建的时候就能创建,不会积压多个相同的节点
    Map<Node, ArrayList<Node>> map2 = new HashMap<>();//链表是因为需要记录下来之前想创建random但是没创建成功的所有节点
    Node res = new Node(0);
    Node resHead = res;
    while (head != null){
        Node newHead = new Node(head.val);
        res.next = newHead;
        map.put(head, newHead);
        if (head.random != null){
            if (map2.containsKey(head.random)){
                map2.get(head.random).add(newHead);
            }else{
                map2.put(head.random, new ArrayList<Node>(){{add(newHead);}});
            }
        }
        if (map2.containsKey(head.random)){
            map2.get(head.random).add(newHead);
        }
        if (map.containsKey(head.random)){
            newHead.random = map.get(head.random);
        }
        if (map2.containsKey(head)){
            ArrayList<Node> list = map2.get(head);
            for (Node node : list) {
                node.random = newHead;
            }
        }
        head = head.next;
        res = res.next;
    }
    return resHead.next;
}

结果:

官方给出的方法一是回溯+哈希表,相当于是遍历了两次

代码如下:

class Solution {
    Map<Node, Node> cachedNode = new HashMap<Node, Node>();

    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        if (!cachedNode.containsKey(head)) {
            Node headNew = new Node(head.val);
            cachedNode.put(head, headNew);
            headNew.next = copyRandomList(head.next);
            headNew.random = copyRandomList(head.random);
        }
        return cachedNode.get(head);
    }
}

方法二省去了哈希表,将该链表中的每一个节点拆分为两个相连的节点

如:A-B-C,拆分为A-A'-B-B'-C-C'

这样,我们可以直接找到每一个拷贝节点 S' 的随机指针应当指向的节点,即为其原节点 S的随机指针指向的节点 T的后继节点 T',即

nodeNew.random = node.random.next
最后拆分即可

代码如下:
 

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        for (Node node = head; node != null; node = node.next.next) {
            Node nodeNew = new Node(node.val);
            nodeNew.next = node.next;
            node.next = nodeNew;
        }
        for (Node node = head; node != null; node = node.next.next) {
            Node nodeNew = node.next;
            nodeNew.random = (node.random != null) ? node.random.next : null;
        }
        Node headNew = head.next;
        for (Node node = head; node != null; node = node.next) {
            Node nodeNew = node.next;
            node.next = node.next.next;
            nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
        }
        return headNew;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值