I came across my question while practicing an (admittedly simple) LeetCode problem. However, my real question is about Python, not the answer to the problem itself. You'll see the full problem statement below, after which I explain my approach, contrast it with the actual solution, and then (finally) ask my question.
Problem:
Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.
Given linked list -- head = [4,5,1,9], which looks like following:

Example 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.
Example 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.
Note:
I fired off a quick answer (which was sub optimal at O(n), but that's not the point) where I reassign the values of the deleted node and all nodes to it's right by shifting them all one unit to the left. In this example, a bracketed node will be reassigned next:
4->[5]->1->9->None becomes4->1->[1]->9->None, then4->1->9->[9]->None, and finally4->1->9->None.Or at least, that's what I expected from the code I wrote below.
# 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
what surprised me about this answer was that the input linked list was exactly the same as the output linked list. Here's a screenshot of the output:

The solution , with a complexity of O(1), is shown below with corresponding (correct) output.
class Solution:
def deleteNode(self, node):
node.val = node.next.val
node.next = node.next.next

Why is it that node.val = node.next.val and node.next = node.next.next modifiy the node of the linked list "in place", while reassigning node in node = node.next has no effect on the object node references?
node = node.next just reassigns the parameter of deleteNode, which doesn't effect anything outside of the function.
Think of it like this: would you expect this to modify x?
x = 1
def f(a):
a = 2
f(x)
It won't. a here is just a local reference inside of f. All reassigning it does is change what object a is pointing to.
Compare that to:
x = []
def f(a):
a.append(2)
f(x)
This will change x. Here, you aren't reassigning the local reference, you're mutating the object that the local reference points to. With your second code, node.val = node.next.val alters a field inside of node. It changes the object.
That's the difference between your two pieces of code. The first piece of code just alters the reference to the object. The second piece of code alters the object itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With