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

题目来源:https://leetcode-cn.com/problems/copy-list-with-random-pointer/

大致题意:
给定一个链表,其中每个节点有两个指针,一个为 next 指向下一个节点,一个为 random 指向随机一个节点(可能为空)。要做的就是复制给定链表。

需要注意,返回结果时,不能返回原链表,也要保持原链表的完整性。

思路

不能直接遍历复制,因为有random指针,其指向的节点可能还未遍历到,也就是还未复制,那么就无法指向该节点。
那么就需要想办法,让新复制的节点的random指针指向正确

哈希表 + 递归

使用一个哈希表存已经创建的节点。键 为 原表节点,值 为 新创建的节点。

  • 每次递归时,先判断当前节点是否为空,若为空直接返回空;若不为空,则判断当前节点是否已经创建,使用HashMap.containsKey方法即可。
  • 若当前节点未创建,则创建,并放入哈希表。再调用递归函数遍历next和random。结束后返回当前节点
  • 若已经创建,直接返回当前节点。

代码:

Map<org.w3c.dom.Node, org.w3c.dom.Node> existed = new HashMap<Node, Node>();
    public Node copyRandomList(Node head) {
        if (head == null) { // 判断是否为空
            return null;
        }
        if (!existed.containsKey(head)) { // 判断是否已经复制
            Node node = new Node(head.val); // 创建
            existed.put(head, node); // 放入Map
            
            // 递归
            node.next = copyRandomList(head.next);
            node.random = copyRandomList(head.random);
        }
        return existed.get(head);
    }
复制 + 剔除

最终返回结果时,要保证原链表是没有改变的。
那么我们可以在解题过程中对其改变!

  1. 第一次遍历,复制每个当前节点,然后新复制的节点的 next 指向 当前节点的 next 指向的节点,再让 当前节点的 next 指向 新复制的节点
  2. 第二次遍历,只遍历原链表的节点,所以循环变量 node 每次都是为next的next。即 node = node.next.next。同时每次让循环变量的next,也就是当前节点的复制节点的 random指针 指向当前节点的random指针指向节点的next(也就是当前节点的random所指节点的复制节点)
  3. 第三次遍历,用作剔除。将复制的节点取出,需要修改next指针。random指针在上一轮已经正确指向。

代码:

public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        // 第一次遍历,复制节点
        for (Node node = head; node != null; node = node.next.next) {
            Node interNew = new Node(node.val);
            // 更改指向,新旧节点串一起
            interNew.next = node.next; 
            node.next = interNew;
        }
        // 第二次遍历,更新random指针
        for (Node node = head; node != null; node = node.next.next) {
            Node interNode = node.next;
            // 修改复制节点的random指针
            // 加上特判,若为空代表random指向空,无next
            interNode.random = (node.random != null) ? node.random.next : null;
        }
        // 创建新链表头结点
        Node newHead = head.next;
        // 第三次遍历,剔除新节点,加入新链表
        for (Node node = head; node != null; node = node.next) {
            Node interNode = node.next;
            // 更改旧节点指向旧节点
            node.next = node.next.next;
            // 更改新节点指向新节点
            // 加上特判,若为尾结点则next为空
            interNode.next = (interNode.next == null) ? null : interNode.next.next;

        }
        return newHead;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值