トピック138. ランダム ポインターを使用したリンク リストのコピーについては、次のように説明します。長さnnのリストが与えられた場合nのリンク リストでは、各ノードは通常のノードよりも追加のランダム ポインタを持ちramdom
、ポインタはリンク リスト内の任意のノードまたは空のノードを指すことができます。このリンクされたリストのディープ コピーを作成します。いわゆるディープ コピーは、異なるメモリ アドレスを持つ新しいオブジェクトを完全に生成するため、コピー前に変数を変更しても、コピーされた変数には影響しません。
ハッシュマップ1
最初に、ユーザー skyliuhc からのより独創的なソリューションを投稿します。公式ソリューションの下のコメントでわかります。彼は Java バージョンを書きました。ここでは私が Python バージョンを書きます。
主なアイデアは、ループを使用して古いリンク リストと新しいリンク リストに対応する 2 つのノードを 2 つのタプルにバンドルし、ループを使用して新しいリンク リストの各ノードへのnext
割り当てrandom
を完了することです。
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head: return None
cur = head
hashmap = {
} # 哈希表
# 第一个循环,建立映射
while cur:
hashmap[cur] = Node(cur.val) # 新旧链表对应结点映射
cur = cur.next # 当前结点往后移动
# 第二个循环,next 和 random 赋值
cur = head
while cur:
# 因为 Python 的字典中不能用 None 作为键值,所以要做一个特殊判断
hashmap[cur].next = hashmap[cur.next] if cur.next else None
hashmap[cur].random = hashmap[cur.random] if cur.random else None
cur = cur.next
return hashmap[head]
ハッシュマップ2
これは、質問をしているときに考えた方法です。前のアイデアと似ています。また、ハッシュ テーブルを使用して、新旧のリンク リストのノードを記録するつもりですが、アイデアはより複雑です。具体的なアイデアは次のとおりです。
- 1. まず、最も一般的なリンク リストを作成し、2 つを記録します
Hashmap
- 1.1、 :
Node2Pos
古いリンクリスト内のそれぞれNode
の位置 - 1.2、
Pos2Node
: 新しいリンクされたリストの各位置は、Node
- 1.1、 :
- 2. 古いリンク リストと新しいリンク リストを同期的に走査して、現在の位置にある古いノードのランダム ノードに対応する位置を取得します
pos
。 - 3.
pos
位置を通じて新しいリンク リストのノードを取得し、現在のノードのランダムがどこを指しているかを知ります。
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head: return None
Node2Pos = {
} # 旧链表的每个 `Node` 的位置
Pos2Node = {
} # 新链表每个位置对应的 `Node`
# 构建新链表
newPreHead = Node(0) # 前置结点
new_ptr = newPreHead # 新链表的头结点
old_ptr = head # 旧链表的头结点
idx = 0 # 当前的位置
while old_ptr:
Node2Pos[old_ptr] = idx # 记录旧结点位置
new_ptr.next = Node(old_ptr.val)
new_ptr = new_ptr.next
Pos2Node[idx] = new_ptr # 记录当前位置的新结点
idx += 1 # 位置也要同步往后
old_ptr = old_ptr.next
Pos2Node[idx] = None # 最后一个位置是空指针 None
# 处理 random node
new_ptr = newPreHead.next
old_ptr = head
while old_ptr:
random_node = old_ptr.random # 获得旧结点的随机结点
# 获得旧结点随机结点的位置,如果为随机结点为空代表是最后一个位置,即 idx
pos = Node2Pos[random_node] if random_node else idx
node = Pos2Node[pos] # 获得对应 pos 的结点
new_ptr.random = node
new_ptr = new_ptr.next
old_ptr = old_ptr.next
return newPreHead.next # 返回结果
バックトラッキング + ハッシュテーブル
公式解決策: バックトラッキング方式を使用して、各ノードのコピー操作を互いに独立させます。現在のノードについては、最初にコピーし、次に「現在のノードの後継ノード」と「ランダム ポインタが指すノード」をコピーします。コピーが完了します。次に、作成した新しいノードのポインタを返し、現在のノードの 2 つのポインタの割り当てを完了します。
具体的な方法は次のとおりです。
- まずハッシュ テーブルを作成し
cachedNode
、ヘッド ノードからトラバースします。 - ノードが空の場合は、 を返します
None
。 val
現在通過しているノード値と同じ値を持つ新しいノードを作成します。- 次に、トラバースして値を
next
とに割り当てますrandom
。
class Solution:
def __init__(self,):
self.cachedNode = {
}
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head: return None
if head not in self.cachedNode.keys():
headNew = Node(head.val) # 拷贝新节点
self.cachedNode[head] = headNew # 记录到哈希表中
headNew.next = self.copyRandomList(head.next)
headNew.random = self.copyRandomList(head.random)
return self.cachedNode[head]
反復 + ノード分割
- リンクされたリストの場合は
A => B => C
、 に変更できますA => A' => B => B' => C => C'
。ここで、はA'
のA
コピー ノードです。
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head: return None
# 将 A => B 变成 A => A' => B => B'
node = head
while node:
nodeNew = Node(node.val)
nodeNew.next = node.next
node.next = nodeNew
node = node.next.next
# 处理 random
node = head
while node:
nodeNew = node.next
nodeNew.random = node.random.next if node.random else None
node = node.next.next
# 将 A => A' => B => B' 变成 A => B
headNew = head.next
node = head
while node:
nodeNew = node.next
node.next = node.next.next
nodeNew.next = nodeNew.next.next if nodeNew.next else None
node = node.next
return headNew