剑指 Offer 35. 复杂链表的复制
https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/
此问题的难点是如果顺序复制,random 指针可能指向未创建的节点。
可以分两次复制,第一次先复制链表,第二次再连接上 random 指针。
在新链表中,为了找到 random 指针指的是谁,需要先访问旧链表找到 random 指针,且需要在新链表中找到对应的节点,如果每次寻找这个节点都要遍历整个链表,时间复杂度则会是 O(N)。
哈希表
使用哈希表记录旧节点到新节点的映射,这样可以快速找到新节点,而不用再遍历。用 O(N) 的空间换了 O(N) 的时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| func copyRandomList(head *Node) *Node { m := make(map[*Node]*Node)
dummyHead := new(Node) currOld := head prev := dummyHead
for currOld != nil { curr := &Node{currOld.Val, nil, nil} prev.Next = curr
m[currOld] = curr
prev = curr currOld = currOld.Next }
curr := head for curr != nil { m[curr].Random = m[curr.Random] curr = curr.Next }
return dummyHead.Next }
|
原地复制 + 拆分链表
可见一个关键是需要知道旧节点和新节点的映射关系。可以在复制链表时,直接把每个新节点接在对应旧节点的后面,变成 old -> new -> old -> new ->
这样的结构。只要顺序遍历这个链表,就知道了旧节点和新节点映射关系,设置完指针后再拆开即可。省去了哈希表的空间开销。
注意需要复原旧链表,而不能仅仅把新链表拆出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| func copyRandomList(head *Node) *Node { if head == nil { return head } curr := head
for curr != nil { next := curr.Next curr.Next = &Node{curr.Val, curr.Next, nil} curr.Next.Next = next
curr = next }
curr = head for curr != nil { if curr.Random != nil { curr.Next.Random = curr.Random.Next } else { curr.Next.Random = nil } curr = curr.Next.Next }
newHead := head.Next curr = head for curr != nil { newNode := curr.Next curr.Next = curr.Next.Next
if newNode.Next != nil { newNode.Next = newNode.Next.Next } curr = curr.Next } return newHead }
|