用一个例子来可视化这个过程可能会有所帮助:
head ┌──────────────────────┐
↓ │ v
┌─────────────┐│ ┌─────────────┐ ┌─────────────┐
│ val: 1 ││ │ val: 2 │ │ val: 3 │
│ next: ───────│─>│ next: ─────────>│ next:None │
│ random: ─────┘ │ random: None│<─────── :random │
└─────────────┘ └─────────────┘ └─────────────┘
所以这个例子是一个有 3 个节点的链表。第一个有一个random 对最后一个节点的引用。最后一个节点有一个random 对中间节点的引用。中间节点没有random 引用(它是None)。
这是第一个循环完成后的结果:
head ┌──────────────────────┐
↓ │ v
┌─────────────┐│ ┌─────────────┐ ┌─────────────┐
│ val: 1 ││ │ val: 2 │ │ val: 3 │
│ next: ───────│─>│ next: ─────────>│ next: None │
│ random: ─────┘ │ random: None│<─────── :random │
└─────────────┘ └─────────────┘ └─────────────┘
oldToCopy oldToCopy oldToCopy
│ │ │
│ │ │
v v v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ val: 1 │ │ val: 2 │ │ val: 3 │
│ next: None │ │ next: None │ │ next: None │
│ random: None│ │ random: None│ │ random: None│
└─────────────┘ └─────────────┘ └─────────────┘
上图的一些解释,现在有很多参考:
- 底部三个节点是使用
new Node() 创建的新节点。
- 这些新节点中的引用(即它们的
next 和random 属性)都是None,因为我们没有(也不能)为这些属性传递任何好的值。新节点的 val 属性设置正确。
-
oldToCopy 是由旧节点键入的字典。给定一个旧节点,它给出相应的新节点。所以这也用向下箭头表示。
现在让我们逐步完成第二个循环。
首先curr和copy定义如下:
curr
head ┌──────────────────────┐
↓ │ v
┌─────────────┐│ ┌─────────────┐ ┌─────────────┐
│ val: 1 ││ │ val: 2 │ │ val: 3 │
│ next: ───────│─>│ next: ─────────>│ next: None │
│ random: ─────┘ │ random: None│<─────── :random │
└─────────────┘ └─────────────┘ └─────────────┘
oldToCopy oldToCopy oldToCopy
│ │ │
│ │ │
v v v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ val: 1 │ │ val: 2 │ │ val: 3 │
│ next: None │ │ next: None │ │ next: None │
│ random: None│ │ random: None│ │ random: None│
└─────────────┘ └─────────────┘ └─────────────┘
↑
copy
然后我们执行copy.next = oldToCopy[curr.next]。我们注意到curr.next 是中间的旧节点。 oldToCopy[curr.next] 是它正下方的节点(新节点)。所以copy.next = oldToCopy[curr.next] 对第一个新节点的next 属性有这种影响:
curr
head ┌──────────────────────┐
↓ │ v
┌─────────────┐│ ┌─────────────┐ ┌─────────────┐
│ val: 1 ││ │ val: 2 │ │ val: 3 │
│ next: ───────│─>│ next: ─────────>│ next: None │
│ random: ─────┘ │ random: None│<─────── :random │
└─────────────┘ └─────────────┘ └─────────────┘
oldToCopy oldToCopy oldToCopy
│ │ │
│ │ │
v v v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ val: 1 │ │ val: 2 │ │ val: 3 │
│ next: ─────────>│ next: None │ │ next: None │
│ random: None│ │ random: None│ │ random: None│
└─────────────┘ └─────────────┘ └─────────────┘
↑
copy
当我们执行copy.random = oldToCopy[curr.random] 时会发生类似的事情。 curr.random 是最后一个旧节点。 oldToCopy[curr.random] 是它正下方的节点。所以copy.random = oldToCopy[curr.random] 对第一个新节点的random 属性有这种影响:
curr
head ┌──────────────────────┐
↓ │ v
┌─────────────┐│ ┌─────────────┐ ┌─────────────┐
│ val: 1 ││ │ val: 2 │ │ val: 3 │
│ next: ───────│─>│ next: ─────────>│ next: None │
│ random: ─────┘ │ random: None│<─────── :random │
└─────────────┘ └─────────────┘ └─────────────┘
oldToCopy oldToCopy oldToCopy
│ │ │
│ │ │
v v v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ val: 1 │ │ val: 2 │ │ val: 3 │
│ next: ─────────>│ next: None │ │ next: None │
│ random: ───────┐│ random: None│ │ random: None│
└─────────────┘ │└─────────────┘ └─────────────┘
↑ │ ^
copy └───────────────────┘
循环的第一次迭代通过将curr 引用移动到原始列表中的下一个节点来完成。此迭代使第一个新节点的next 和random 属性引用新节点而不是旧节点。
下一次迭代将对第二个新节点执行相同的操作——random 曾经是并且仍然是None,但 next 将被重新连接——因此我们得到:
┌──────────────────────┐
head │ curr │
↓ │ ↓ v
┌─────────────┐│ ┌─────────────┐ ┌─────────────┐
│ val: 1 ││ │ val: 2 │ │ val: 3 │
│ next: ───────│─>│ next: ─────────>│ next: None │
│ random: ─────┘ │ random: None│<─────── :random │
└─────────────┘ └─────────────┘ └─────────────┘
oldToCopy oldToCopy oldToCopy
│ │ │
│ │ │
v v v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ val: 1 │ │ val: 2 │ │ val: 3 │
│ next: ────────> │ next: ─────────>│ next: None │
│ random: ───────┐│ random: None│ │ random: None│
└─────────────┘ │└─────────────┘ └─────────────┘
│ ↑ ^
│ copy │
└────────────────────┘
最后一次迭代将对最后一个新节点做同样的事情:
head ┌──────────────────────┐ curr
↓ │ v ↓
┌─────────────┐│ ┌─────────────┐ ┌─────────────┐
│ val: 1 ││ │ val: 2 │ │ val: 3 │
│ next: ───────│─>│ next: ─────────>│ next: None │
│ random: ─────┘ │ random: None│<─────── :random │
└─────────────┘ └─────────────┘ └─────────────┘
oldToCopy oldToCopy oldToCopy
│ │ │
│ │ │
v v v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ val: 1 │ │ val: 2 │ │ val: 3 │
│ next: ────────> │ next: ─────────>│ next: None │
│ random: ───────┐│ random: None│<─────── :random │
└─────────────┘ │└─────────────┘ └─────────────┘
│ ^ ↑
└────────────────────┘ copy
最后,函数返回oldToCopy[head],这是对克隆列表的引用,现在不再引用旧节点。