LeetCode138 随机链表的复制

51 阅读2分钟

leetcode.cn/problems/co… image.png

解法一:哈希法

对于数据结构复制,甭管他怎么变,你就记住最简单的方式:一个哈希表 + 两次遍历

第一次遍历专门克隆节点,借助哈希表把原始节点和克隆节点的映射存储起来;第二次专门组装节点,照着原数据结构的样子,把克隆节点的指针组装起来。

/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Next *Node
 *     Random *Node
 * }
 */

func copyRandomList(head *Node) *Node {
    // 创建map,映射链表上的新旧节点
    memo := make(map[*Node]*Node)
    // 第一次遍历,申请新节点
    cur := head
    for cur != nil{
        if _, ok := memo[cur]; !ok{
            memo[cur] = &Node{Val: cur.Val}
        }
        cur = cur.Next
    }
    // 第二次遍历,复制链表的指向关系
    cur = head
    for cur != nil{
        if cur.Next != nil{
            memo[cur].Next = memo[cur.Next]
        }
        if cur.Random != nil{
            memo[cur].Random = memo[cur.Random]
        }
        cur = cur.Next
    }
    return memo[head]
}
  • 时间复杂度 O(N) :  两轮遍历链表,使用 O(N) 时间。
  • 空间复杂度 O(N) :  哈希表使用线性大小的额外空间。

解法二:原链表上拼接+拆分

考虑构建 原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表,便可在遍历原链表的过程,访问原节点的 random 指向的同时,找到新对应新节点的 random 指向节点。

/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Next *Node
 *     Random *Node
 * }
 */

func copyRandomList(head *Node) *Node {
    if head == nil{
        return nil
    }
    cur := head
    // 构建原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表
    for cur != nil{
        tmp := &Node{
            Val: cur.Val,
            Next: cur.Next,
        }
        cur.Next = tmp
        cur = tmp.Next
    }
    // 构建新链表各节点的random指向
    cur = head
    for cur != nil{
        if cur.Random != nil{ 
            cur.Next.Random = cur.Random.Next
        }
        cur = cur.Next.Next // 跳过中间复制的新节点
    }
    // 拆分链表
    res := head.Next // 新链表的头节点
    pre, cur := head, head.Next // 两个指针分别指向原链表和新链表的头节点
    for pre.Next != nil && cur.Next != nil{ // (可省略pre的判断)
        // 串接原链表
        pre.Next = pre.Next.Next
        pre = pre.Next
        // 串接新链表
        cur.Next = cur.Next.Next
        cur = cur.Next
    }
    pre.Next = nil // 断开原链表的尾节点和复制节点的连接
    return res
}
  • 时间复杂度 O(N) :  三轮遍历链表,使用 O(N) 时间。
  • 空间复杂度 O(1) :  仅使用了额外的指针变量。