正如这里提到的,如果我有两个变量
x
&y
x = 0
y = x
y += 1
print (x) #prints 0
此处代码将打印 0,因为将
x
分配给 y
,然后更改 y
不会更改 x
。class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode()
#assign dummy to tail
tail = dummy
while list1 and list2:
if list1.val < list2.val:
tail.next = list1
list1 = list1.next # Move list1 pointer forward
else:
tail.next = list2
list2 = list2.next # Move list2 pointer forward
tail = tail.next
if list1:
tail.next = list1
elif list2:
tail.next = list2
#return dummy.next that is head of resultant linkedlist
return dummy.next
即使我们不对虚拟对象执行任何操作,而是将所有节点添加到尾部,但当我们最终必须返回结果链表的头时,我们可以这样做
dummy.next
。我不明白为什么?
变量(名称)赋值和变异(属性赋值)之间的区别:
y += 1
是变量赋值,不执行(对象)突变。
tail.next = list1
是一个属性分配,会改变任何 tail
引用。
在链表算法中,
tail
和dummy
是两个内存位置,它们都以对新节点的引用开始。它们引用 same 节点(带有虚拟值)。让我们假设两个列表的简单输入,每个列表都有一个节点:
┌───────────┐
list1:─────┤ val: 1 │
│ next: None│
└───────────┘
┌───────────┐
dummy:─────┤ val: 0 │
tail:──────┤ next: None│
└───────────┘
┌───────────┐
list2:─────┤ val: 2 │
│ next: None│
└───────────┘
现在第一次迭代就有了线索。它执行
tail.next = list1
,从而改变 tail
引用的对象。它不是对 tail
的赋值,因此 tail
保持相同的引用(值),但它是被更新的引用对象,因此如果您通过以下方式查看该对象,预计您会看到此更改dummy
:
┌───────────┐
list1:─────┤ val: 1 │
┌──────┤ next: None│
│ └───────────┘
│
┌────────│──┐
dummy:─────┤ val: 0 │ │
tail:──────┤ next: ─┘ │
└───────────┘
┌───────────┐
list2:─────┤ val: 2 │
│ next: None│
└───────────┘
此时你的问题已经得到解答,但让我继续。接下来
list1
和 tail
是 分配 新引用,因此它们现在引用其他内容:
┌───────────┐
tail:─────┤ val: 1 │ list1: None
┌──────┤ next: None│
│ └───────────┘
│
┌────────│──┐
dummy:─────┤ val: 0 │ │
│ next: ─┘ │
└───────────┘
┌───────────┐
list2:─────┤ val: 2 │
│ next: None│
└───────────┘
一旦执行了第一次迭代,tail
和dummy
就不再引用同一个对象,因此tail
的另一个突变将不再突变dummy
引用的对象。但由于它是一个链表,如果您从 dummy
开始遍历,则更改将进一步“沿着道路”。
在此示例中,循环在第一次迭代后已退出。这次执行了
tail.next = list2
,这样就完成了合并列表:
┌───────────┐
tail:─────┤ val: 1 │ list1: None
┌──────┤ next: ─┐ │
│ └────────│──┘
│ │
┌────────│──┐ │
dummy:─────┤ val: 0 │ │ │
│ next: ─┘ │ │
└───────────┘ │
┌───────────┘
│ ┌───────────┐
└──┤ val: 2 │
list2:─────┤ next: None│
└───────────┘
现在很明显,
dummy.next
引用了在循环的第一次迭代中分配给tail.next
的节点。