user6703592
user6703592

Reputation: 1136

Python Logic of ListNode in Leetcode

Here is the definition of ListNote class in LeetCode:

class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

For the code:

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

The values in comments are from my guessing. I cannot understand the step

result_tail = result_tail.next

result_tail = result is pass by reference, so when result_tail becomes 1 -> None, result should also become 1 -> None. Why does result still keep 0 -> 1 -> None? And when result_tail becomes 1 -> 2 -> None, why does result extend its tail to 0 -> 1 -> 2 -> None?

result_tail = result_tail.next

is something like

result_tail = result.next.next    

Can anyone tell me the logic here?

Upvotes: 35

Views: 109812

Answers (4)

nihal dixit
nihal dixit

Reputation: 53

All above answers seems good. I am just adding up an example for the reader's understanding.

Input given : [[1,4,5],[1,3,4],[2,6]]

ListNode object description:

[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}}]

Hope you got the CRUX!

Upvotes: 3

Neeglug
Neeglug

Reputation: 161

For those reading this in the future: I wanted to debug linked list problems on a local environment so here is what I did.

  1. Modified the Leetcode code for ListNode by including the dunder "repr" method. This is for when you want to print a ListNode to see what its value and next node(s).
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) + "})"
  1. Next, I made a recursive function that makes a nested ListNode when you pass in a list. This is so you can test your methods by passing in lists (instead of having to manually make a confusing looking ListNode yourself.
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:]))
  1. Here is an example that tests my answer for the "reverseList" problem:
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))

Upvotes: 16

Linlin
Linlin

Reputation: 63

First, thank you so much for posting this question. I worked on the same problem and saw this piece of code and was puzzled too. Then I followed some comments from leetcode and came here.

I realize that my problem was that I didn't have a pen and paper earlier. After I drew the linked list on the paper by following the loop, it turned out to be quite clear.

If you are still not clear about this, please try to draw the linked list by following the logic. Not sure if I got the right term here but below is my understanding.

To be honest, I do not think this is related to pass by reference or value. To me, this is just about two variables being assigned with the same value(memory location) at the beginning. Think of variables as storage of address. Address is the real memory location which is the start of some value. Later on, one variable(result_tail) kept getting reassigned to a different location and one(result) stays the same.

Result and result_tail both point to the location of 0|None before the while loop.
0|None grew into 0->7|None, then 0->7->0|None and at last 0->7->0->8|None by result_tail.next being assigned every time. Result_tail gets reassigned so value changed during each loop, but result points to the same location which is the 0->.... Thus the result.

Upvotes: 4

Joseph
Joseph

Reputation: 467

The short answer to this is that, Python is a pass-by-object-reference language, not pass-by-reference as implied in the question. It means that:

  1. result and result_tail are two variables that happen to point at the same value
  2. Mutation / Changing of the underlying value (result_tail.next = ListNode(1)) will affect the value shown by result
  3. However, assigning / pointing the variable result_tail to another value will NOT affect the value of result
  4. result_tail = result_tail.next is assigning the next node of the node that is currently assigned by the variable

The following is an visualization of the values that are assigned to the variables (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

References for additional reading:

Upvotes: 27

Related Questions