Card 04/ 08
Linked List Reversal Example: Reversing One Stretch With a Dummy Node
Reversing positions a to b needs the node before a, and a dummy node in front of the head gives position 1 such a node too.
Reversing one stretch of a list, positions a to b counted from 1, uses the same four lines on b − a + 1 nodes and then reconnects the stretch to the nodes on either side. A dummy node is an extra node placed in front of the head that holds no real value, so that every real node, the head included, has a node before it. On 10 20 30 40 50, reversing positions 2 to 4 should give 10 40 30 20 50.
The walk has three parts. Move before from the dummy to the node just before position a. Reverse the stretch with prev starting at null. Then reconnect: the stretch's old first node, still before.next, is now its last, so its next becomes curr, the node after the stretch; and before.next becomes prev, the stretch's new first node.
Reversing positions 2 to 4 of 10 20 30 40 50
Walk to the node before position 2
dummy: dummy → 10 → 20 → 30 → 40 → 50 → null
before: 10
curr: 20 → 30 → 40 → 50 → nullReverse 20
before: 10
prev: 20 → null
curr: 30 → 40 → 50 → nullReverse 30
before: 10
prev: 30 → 20 → null
curr: 40 → 50 → nullReverse 40
before: 10
prev: 40 → 30 → 20 → null
curr: 50 → nullReconnect the stretch
after: before.next.next = curr, then before.next = prev
dummy: dummy → 10 → 40 → 30 → 20 → 50 → null
returned: 10 → 40 → 30 → 20 → 50 → nullWhile the stretch is reversed, 10 still points at 20, which has become the stretch's last node; the two reconnecting lines put 40 after 10 and 50 after 20. The listing runs the same function on positions 2 to 4 and on positions 1 to 2.
class ListNode:
def __init__(self, val):
self.val = val
self.next = None
def build(values):
head = None
for val in reversed(values):
node = ListNode(val)
node.next = head
head = node
return head
def values_of(head):
values = []
while head:
values.append(head.val)
head = head.next
return values
def reverse_between(head, left, right):
dummy = ListNode("dummy")
dummy.next = head
before = dummy
for _ in range(left - 1):
before = before.next
prev, curr = None, before.next
for _ in range(right - left + 1):
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
before.next.next = curr
before.next = prev
return dummy.next
print("positions 2 to 4:", *values_of(reverse_between(build([10, 20, 30, 40, 50]), 2, 4)))
print("positions 1 to 2:", *values_of(reverse_between(build([10, 20, 30, 40, 50]), 1, 2)))positions 2 to 4: 10 40 30 20 50
positions 1 to 2: 20 10 30 40 50Positions 1 to 2 are the case the dummy exists for. Without it the head has no node before it, so a = 1 would need its own branch that changes which node the function returns. With it, before stays on the dummy, the same two lines reconnect, and dummy.next is the new head whichever stretch was reversed. The walk is O(b) time and O(1) extra space.
← → move through sections and cards