Most linked-list problems use a few techniques: rewiring the next pointers, two pointers (slow/fast or with a gap) and a dummy head. The Node class is familiar from the linked lists topic: Node(value, next).

Reversing a list: on every step one link flips its direction
Reversing a list: on every step one link flips its direction

Helper functions

For tests it is handy to build a list from an array and convert it back.

python
class Node:
    def __init__(self, value, next=None):
        self.value = value
        self.next = next

def build(items):
    head = None
    for v in reversed(items):
        head = Node(v, head)
    return head

def to_list(head):
    out = []
    while head:
        out.append(head.value)
        head = head.next
    return out

print(to_list(build([1, 2, 3])))
text
[1, 2, 3]

Reversing a list

Keep three pointers: prev (the head of the already reversed part), head (the current node) and nxt (the next node that must not be lost). On every step point head.next at prev. Time O(n), memory O(1).

python
def reverse(head):
    prev = None
    while head:
        nxt = head.next
        head.next = prev
        prev = head
        head = nxt
    return prev

print(to_list(reverse(build([1, 2, 3, 4]))))
print(to_list(reverse(None)))
text
[4, 3, 2, 1]
[]

💡 Write nxt = head.next before head.next = prev, otherwise the rest of the list is lost.

The middle: slow and fast pointers