提问人:user6703592 提问时间:6/9/2019 最后编辑:user6703592 更新时间:6/22/2021 访问量:85169
Leetcode 中 ListNode 的 Python 逻辑
Python Logic of ListNode in Leetcode
问:
以下是 class 的定义:ListNote
LeetCode
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
对于代码:
result = ListNode(0)
#result = 0 -> None
result_tail = result
#result_tail = 0 -> None
result_tail.next = ListNode(1)
#result_tail = 0 -> 1 -> None
#result = 0 -> 1 -> None
result_tail = result_tail.next
#result_tail = 1 -> None
#result = 0 -> 1 -> None
result_tail.next = ListNode(2)
#result_tail = 1 -> 2 -> None
#result = 0 -> 1 -> 2 -> None
result_tail = result_tail.next
#result_tail = 2 -> None
#result = 0 -> 1 -> 2 -> None
评论中的值来自我的猜测。我看不懂这一步
result_tail = result_tail.next
result_tail = result
是引用传递的,所以当成为时,也应该成为。为什么仍然保留?而当变成 时,为什么会把尾巴伸到?result_tail
1 -> None
result
1 -> None
result
0 -> 1 -> None
result_tail
1 -> 2 -> None
result
0 -> 1 -> 2 -> None
result_tail = result_tail.next
是这样的
result_tail = result.next.next
谁能告诉我这里的逻辑?
答:
对此的简短回答是,Python 是一种按对象引用传递的语言,而不是问题中暗示的按引用传递。这意味着:
result
并且是恰好指向相同值的两个变量result_tail
- 基础值 () 的突变/更改将影响
result_tail.next = ListNode(1)
result
- 但是,将变量赋值/指向另一个值不会影响
result_tail
result
result_tail = result_tail.next
正在分配当前由变量分配的节点的下一个节点
以下是分配给变量 ( = , = ) 的值的可视化效果:r
result
rt
result_tail
result = ListNode(0)
#r
#0 -> None
result_tail = result
#r
#0 -> None
#rt
result_tail.next = ListNode(1)
#r
#0 -> 1 -> None
#rt
result_tail = result_tail.next
#r
#0 -> 1 -> None
# rt
result_tail.next = ListNode(2)
#r
#0 -> 1 -> 2 -> None
# rt
result_tail = result_tail.next
#r
#0 -> 1 -> 2 -> None
# rt
其他阅读的参考资料:
- 一篇详细解释 Python 逐对象传递引用样式的文章 https://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/
- 解释 Python 的按对象传递引用样式的答案 https://stackoverflow.com/a/33066581/12295149
- 关于 Python 对象引用样式的问题 了解 Python 传递函数参数的按对象调用样式
首先,非常感谢您发布这个问题。我处理了同样的问题,看到了这段代码,也感到困惑。然后我听从了 leetcode 的一些评论并来到了这里。
我意识到我的问题是我之前没有笔和纸。我顺着循环在纸上画了链表后,结果发现很清楚。
如果您仍然不清楚这一点,请尝试按照逻辑绘制链表。不确定我在这里是否得到了正确的术语,但以下是我的理解。
老实说,我不认为这与通过引用或值传递有关。对我来说,这只是在开始时为两个变量分配了相同的值(内存位置)。将变量视为地址的存储。地址是真正的内存位置,它是某个值的开始。后来,一个变量(result_tail)不断被重新分配到不同的位置,而一个变量(结果)保持不变。
Result 和 result_tail 都指向 0|while 循环之前没有。
成长为,然后,最后由result_tail.next每次被分配。Result_tail被重新分配,因此值在每个循环中都发生了变化,但结果指向相同的位置,即结果。0|None
0->7|None
0->7->0|None
0->7->0->8|None
0->....
评论
对于那些将来阅读本文的人:我想在本地环境中调试链表问题,所以这是我所做的。
- 修改了 ListNode 的 Leetcode 代码,包含 dunder “repr” 方法。这适用于要打印 ListNode 以查看其值和下一个节点的情况。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def __repr__(self):
return "ListNode(val=" + str(self.val) + ", next={" + str(self.next) + "})"
- 接下来,我制作了一个递归函数,当你传入一个列表时,它会创建一个嵌套的 ListNode。这样你就可以通过传入列表来测试你的方法(而不必自己手动制作一个看起来令人困惑的 ListNode。
def list_to_LL(arr):
if len(arr) < 1:
return None
if len(arr) == 1:
return ListNode(arr[0])
return ListNode(arr[0], next=list_to_LL(arr[1:]))
- 下面是一个示例,用于测试我对“reverseList”问题的回答:
def reverseList(head: ListNode) -> ListNode:
prev = None
while head:
next_node = head.next
head.next = prev
prev = head
head = next_node
return prev
# test cases
t1 = list_to_LL([1, 2, 3, 4, 5]) #ListNode(val=1, next={ListNode(val=2, next={ListNode(val=3, next={ListNode(val=4, next={ListNode(val=5, next={None})})})})})
t2 = list_to_LL([1, 2]) #ListNode(val=1, next={ListNode(val=2, next={None})})
t3 = list_to_LL([])
# answers
print(reverseList(t1))
print(reverseList(t2))
print(reverseList(t3))
以上所有答案似乎都不错。我只是举个例子供读者理解。
输入给出:[[1,4,5],[1,3,4],[2,6]]
ListNode 对象说明:
[ListNode{val: 1, next: ListNode{val: 4, next: ListNode{val: 5, next: None}}}, ListNode{val: 1, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}, ListNode{val: 2, next: ListNode{val: 6, next: None}}]
希望你得到了 CRUX!
评论