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

178 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、题目描述

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

示例

示例1: image.png

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

示例2: image.png

输入: head = [[1,1],[2,1]]
输出: [[1,1],[2,1]]

示例3: image.png

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

示例4:

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

提示

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

难度:中等

复杂链表的复制,链表结构确实复杂,题目不太好理解,看用例数量、又带图就能说明问题。

二、思路分析

众所周知,力扣上,简单题是真简单,中等题有些困难,困难题看不懂答案。

题目解析

  1. 复杂链表的复制,题目给到的链表结点比常规链表结点多了一个 random 结点,而这个结点是无序的,并且根据链表的特点,next 指针的走向不可逆,即无法再找到已经走过的结点。
  2. 基于上述问题,在复制结点时,不仅要复制当前结点的 next 结点和 random 结点,并且需要记录新旧结点的关系,以便在其他结点指向该结点时能够对应指向复制的新结点。
  3. 综上,在遍历链表结点时考虑 next 和 random 结点指向,并将当前结点和复制新结点关系存储在 map 中,在每次关联 next 和 random 结点时判断 map 中是否已经存在,存在则直接使用,不存在则复制后添加到 map。

解题思路

对于复杂链表的复制,具体实现流程为:

  1. 初始化 map 容器用来存放新旧结点对应关系,其中原始结点作为 key,新结点作为 value;
  2. 将 head 结点复制到新结点,并将关联关系加入到 map 中;
  3. 从 head 开始遍历整个整个链表,并在遍历过程中进行如下操作:
    • 对于 next 指向结点
      • 如果为 null,则复制结点指向 null;
      • 如果非 null,则判断 map 中是否存在指向结点,存在则复制结点指向 map 中对应的value;如果不存在,则指向新复制结点,并将旧-新关系加入到 map 容器。
    • 对于 random 指向结点操作方式与 next 一致
    • 操作完成后,新旧链表中的当前结点指针都前进一步,继续进行遍历迭代操作
  4. 遍历完整个链表之后,map 中 head 结点对应的 value 就是新链表的头节点,返回即可。

三、AC 代码

  • 代码定义类结构说明
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}

实现代码

public Node copyRandomList(Node head) {
    if(head == null) return null;
    //使用集合存放每个操作过的结点,用于在random 时直接指向
    HashMap<Node,Node> map = new HashMap<>();
    Node result = new Node(head.val);
    Node cur = result;
    map.put(head,result);
    while(head != null){
        //next结点
        Node next = head.next;
        if(next == null){
            cur.next = null;
        }else if(map.containsKey(next)){
            cur.next = map.get(next);
        }else{
            cur.next = new Node(next.val);
            map.put(next,cur.next);
        }

        //random节点
        Node random = head.random;
        if(random == null){
            cur.random = null;
        }else if(map.containsKey(random)){
            cur.random = map.get(random);
        }else{
            cur.random = new Node(random.val);
            map.put(random,cur.random);
        }

        head = head.next;
        cur = cur.next;
    }
    return result;
}

执行结果

image.png

四、总结

知识点

  • 容器 map 中 key、value 都是链表结点,因此在比较是否相等时直接使用 == 来比较结点为同一个对象

最后

阳春三月,算法刷起来!LeetCode 剑指 Offer