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

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

题目描述:

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

示例:

Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]

image-20211103171808636

解题思路:

思路一: 使用hash Map 存储链表的结点,其中,原链表结点为key,value为该结点的复制结点。首先遍历一遍链表,将每个结点都进行复制。其次,将原结点的next结点通过get将其复制结点取出,并且让复制结点的next指向该结点。同理,random结点也是这样的做法。可能文字有点表达不清,通过代码注释

class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;  
        HashMap<Node,Node> temp = new HashMap<>();  // 创建一个哈希表来存储结点映射关系
        Node cur = head;
        while(cur!=null){
            temp.put(cur,new Node(cur.val));  // 将原结点遍历一遍,原结点作为key,复制结点作为value。
            cur = cur.next;
        }
        cur = head;
        while(cur!=null){
            temp.get(cur).next = temp.get(cur.next);  // temp.get(cur) 取出的是cur对应的复制结点。 temp.get(cur.next) 取出的是cur.next对应的复制结点,通过这样形成连接关系。
            temp.get(cur).random = temp.get(cur.random); // 同理。
            cur = cur.next;
        }
        return temp.get(head);

    }
}

思路二: 原地操作,不使用额外的空间存储链表。首先原地复制链表,即原结点指向该原结点的复制结点。这样的好处是,每个原结点的下一个为它的复制结点,原结点的下下个结点为其原链表的对应下一个结点。

image-20211103183727354

因此在完成结点的复制后,复制后的结点的next,则可以通过原结点的next结点的next找到。比如7的next为3,那么赋值后的7’则是通过7.next=3, 3.next = 3’ 找到对应关系。同理random关系也是这么找。

class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;
        Node cur = head;
        Node next = null;
        while(cur != null){  // 首先进行结点的复制与拼接
            next = cur.next;  // 先将当前结点的下一个结点保存
            cur.next = new Node(cur.val);  // 让当前结点的下一个结点指向它的复制结点。
            cur.next.next = next;  // 让复制结点的下一个结点指向原结点的下一个结点。
            cur = next;  // 将当前结点指针移到之前保存的位置
        }
        cur = head;
        next = head.next;
        while(cur != null){  
            next = cur.next;  // 指向复制结点
            next.random = cur.random != null ? cur.random.next : null;  // 如果cur.random 不为空,说明复制结点的random也不为空,且为cur.random.next
            cur = cur.next.next; // cur指针移向下一个原链表的结点。
        }

        Node res = head.next; // 保存结果
        cur = head; 
        Node temp1 = nullwhile(cur!=null){  // 将原链表和复制链表拆开
            next = cur.next.next;  // 先保存原链表的下一个结点
			temp1 = cur.next;  // 保存复制结点
			cur.next = next;  // 将原结点指向下下个结点,也就是原链表的对应关系
			temp1.next = next != null ? next.next : null;  // 复制链表的下一个结点为next的下一个结点,但是此时要保证next不为空,否则无法指向。
			cur = next;
        }
        return res;
    }
}

该方法需要注意的是,在循环内,当指针向下下个结点移动时,不能复制后的结点的指针一次跳两个,而应该选择原结点的下一个结点这样来作为复制结点,而不是复制结点直接跳到它的下下个结点,这样可能会产生空指针异常。

image-20211103190014373

复杂链表的复制就是这样啦,可能文字方面讲得不够好,所以我在代码里加了注释,希望对你能有所帮助!

如有错误,欢迎指出,大家一起进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值