在python3中,对分配给另一个节点的空链表节点执行操作会导致原始节点发生变化吗?

问题描述 投票:0回答:1

正如这里提到的,如果我有两个变量

x
y

x = 0
y = x
y += 1
print (x)  #prints 0

此处代码将打印 0,因为将

x
分配给
y
,然后更改
y
不会更改
x

然而,在下面的代码中,用于合并两个已排序的链表(leetcodesolution),然后返回结果链表的头:

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
。我不明白为什么?

python-3.x linked-list variable-assignment
1个回答
0
投票

变量(名称)赋值和变异(属性赋值)之间的区别:

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
的节点。

© www.soinside.com 2019 - 2024. All rights reserved.