【问题标题】:Modify linked list node "in place" in Python在 Python 中“就地”修改链表节点
【发布时间】:2019-11-17 00:00:34
【问题描述】:

我在练习一个(诚然简单的)LeetCode 问题时遇到了我的问题。然而,我真正的问题是关于 Python,而不是问题本身的答案。您将在下面看到完整的问题陈述,然后我会解释我的方法,将其与实际解决方案进行对比,然后(最终)提出我的问题。


LeetCode 问题:Delete Node in Linked List

问题:

编写一个函数来删除单链表中的一个节点(除了尾部),只允许访问该节点。

给定链表 --head = [4,5,1,9],如下所示:

示例 1:

Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list should become 4 -> 1 -> 9 after calling your function.

示例 2:

Input: head = [4,5,1,9], node = 1
Output: [4,5,9]
Explanation: You are given the third node with value 1, the linked list should become 4 -> 5 -> 9 after calling your function.

注意:

  • 链表至少有两个元素。
  • 所有节点的值都是唯一的。
  • 给定的节点不会是尾节点,它始终是链表的有效节点。
  • 不要从您的函数中返回任何内容。

我的方法:

我给出了一个快速的答案(这在 O(n) 时是次优的,但这不是重点),我将已删除节点和所有节点的值重新分配到它的右边,方法是将它们全部向左移动一个单位.在此示例中,接下来将重新分配括号中的节点:

  1. 4->[5]->1->9->None 变为
  2. 4->1->[1]->9->None,然后
  3. 4->1->9->[9]->None,最后是
  4. 4->1->9->None

或者至少,这是我对下面编写的代码的期望。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, node):
        while node != None:
            node = node.next

这个答案让我惊讶的是输入链表与输出链表完全相同。这是输出的屏幕截图:


实际解决方案:

solution 的复杂度为 O(1),如下所示,并带有相应的(正确)输出。

class Solution:
    def deleteNode(self, node):
        node.val = node.next.val
        node.next = node.next.next


我的问题:

为什么node.val = node.next.valnode.next = node.next.next“就地”修改了链表的节点,而在node = node.next中重新赋值node对对象node的引用没有影响?

【问题讨论】:

    标签: python linked-list singly-linked-list


    【解决方案1】:

    node = node.next 只是重新分配了deleteNode 的参数,不会影响函数之外的任何内容。

    可以这样想:你希望这会修改x吗?

    x = 1
    
    def f(a):
        a = 2
    
    f(x)
    

    不会的。 a 这里只是 f 内部的一个本地引用。重新分配它所做的只是改变 a 指向的对象。

    比较一下:

    x = []
    
    def f(a):
        a.append(2)
    
    f(x)
    

    这将改变x。在这里,您不是在重新分配本地引用,而是在改变本地引用指向的对象。使用您的第二个代码,node.val = node.next.val 会更改 node 内部的一个字段。它改变了对象。

    这就是您的两段代码之间的区别。第一段代码只是改变了对对象的引用。第二段代码改变了对象本身。

    【讨论】:

    • 我理解你的回答,但我认为关于 python 的一些更一般的东西我没有得到。假设我们有一个真实的节点对象real 和该对象的标识符ident。我的问题是如果ident 只是real 的标识符,为什么ident.val 不是real.val 的标识符?换句话说,如果更改identreal 没有影响,为什么ident.val = ident.next.val 会更改real 内部的字段(它会更改对象),而不仅仅是更改ident.val 引用的内容?跨度>
    • 换句话说,有没有很好的解释为什么这是真的:“node.val = node.next.val 改变了节点内部的一个字段。它改变了对象。”在标识符、对象绑定方面?
    • @David 这不是 Python 特有的。 Java 中也会有类似的行为。让我看看我是否能找出一个有意义的书面解释。如果我们面前有一块白板,我可以解释它,因为这就是我学习它的方式,也是我至今在脑海中想象它的方式。我更像是一个视觉型的人。
    • 其实我采纳了你的想法,找了一块白板看看能不能自己解释一下。这是我最好的选择:标识符没有属性,只有对象有。当ident 成为real 的标识符时,这并不意味着它成为某种“标识符对象”,其属性是real 的所有属性的标识符,例如ident.val->real.valident.next->real.nextident 只是绑定到real 的单个标识符(单个箭头)。通过ident,我们可以访问real的属性,这就是我们写ident.valident.next时发生的情况
    • @Dave 这实际上就是我想要的。我通常通过写下对象的名称 (node) 来可视化这一点,然后是从它指向某个对象的箭头(通常是一个用于可视化的圆圈)。名称和对象是独立的实体。当您在程序中使用像someVar.append() 这样的名称时,它会查找someVar 名称与哪个对象相关联,并在其上调用appendsomeVar 不是其他具有自己状态的列表对象。我认为它是一个指向对象的标签。
    猜你喜欢
    • 1970-01-01
    • 2019-02-20
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    • 2015-11-07
    • 2019-03-18
    相关资源
    最近更新 更多