剑指offer-35

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

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

示例

image-20210617160909209

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

示例

image-20210617161115322

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

解法

方法一:暴力解法

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
42
43
44
45
46
47
48
49
50
51
52
53
54
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
'''
先不管随机节点,随机保存的是地址索引
'''
newHead = Node(x=0)
p = newHead

cur = head
hash_list = []
count_list = []
while cur:
t = Node(x=cur.val)
hash_list.append(t) # 保存
p.next = t
p = t

# 记录索引位置
randomTarget = cur.random
new_p = head
count = 0
# print(new_p,randomTarget)
while new_p != randomTarget:
count += 1
new_p = new_p.next
count_list.append(count)
'''
或者在这里找到其random对应的节点,在原链表中的位置
'''
cur = cur.next

# 在这里重建随机索引
p = newHead.next # 新的头结点
cur = head # 原头结点

count = 0
while p:

nodeIndex = count_list[count] # 获取随机节点,在链表中的位置
try:
p.random = hash_list[nodeIndex]
except:
p.randomTarget = None
p = p.next
count += 1
return newHead.next

以下为官方解法

剑指 Offer 35. 复杂链表的复制(哈希表 / 拼接与拆分,清晰图解) - 复杂链表的复制 - 力扣(LeetCode) (leetcode-cn.com)

方法一:哈希表

利用哈希表的查询特点,考虑构建 原链表节点新链表对应节点 的键值对映射关系,再遍历构建新链表各节点的 nextrandom 引用指向即可。

算法流程

  1. 若头节点 head 为空节点,直接返回 null
  2. 初始化: 哈希表 dic , 节点 cur 指向头节点;
  3. 复制链表:
    1. 建立新节点,并向 dic 添加键值对 (原 cur 节点, 新 cur 节点)
    2. cur 遍历至原链表下一节点;
  4. 构建新链表的引用指向:
    1. 构建新节点的 nextrandom 引用指向;
    2. cur 遍历至原链表下一节点;
  5. 返回值: 新链表的头节点 dic[cur]
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
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if not head: return

dic = {}
# 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
cur = head
while cur:
dic[cur] = Node(cur.val)
cur = cur.next

cur= head
# 4. 构建新节点的 next 和 random 指向
while cur:
dic[cur].next = dic.get(cur.next) # 在字典中获取原链表的下一个节点
dic[cur].random = dic.get(cur.random)
cur = cur.next
# 5. 返回新的阶段的头结点
return dic[head]

方法二:拼接 + 拆分

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

算法流程:

  1. 复制各节点,构建拼接链表:

    • 设原链表为 $node1 \rightarrow node2 \rightarrow \cdots$ ,构建的拼接链表如下所示:
      $$
      node{1} \rightarrow \text { node } 1 _ { \text {new } } \rightarrow \text { node } 2 \rightarrow \text { node } 2 \text { new } \rightarrow \cdots
      $$
  2. 构建新链表各节点的 random 指向:

    • 当访问原节点 cur 的随机指向节点 cur.random 时,对应新节点 cur.next 的随机指向节点为 cur.random.next
  3. 拆分原 / 新链表:

    • 设置 pre / cur 分别指向原 / 新链表头节点,遍历执行 pre.next=pre.next.nextcur.next = cur.next.next 将两链表拆分开。
  4. 返回新链表的头节点 res 即可。

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
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if not head: return

cur = head
# 1. 复制各节点,并构建拼接链表
while cur:
temp = Node(cur.val)
temp.next = cur.next
cur.next = temp
cur = temp.next
# 2. 构建各新节点的 random 指向
cur = head
while cur:
if cur.random:
cur.next.random = cur.random.next
# 这里是因为,原先链表经过复制,每个节点后面都跟了一他的拷贝
cur = cur.next.next
# 3. 拆分两链表
cur = res = head.next
pre = head

while cur.next:
# 先更新下一个节点,这里对应拆分
pre.next = pre.next.next
cur.next = cur.next.next
pre=pre.next
cur = cur.next
pre.next = None # 单独处理原链表尾节点
return res # 返回新链表头节点

作者

bd160jbgm

发布于

2021-06-17

更新于

2021-06-17

许可协议