For removing nodes with a given value, using a sentinel node avoids handling the head as a special case. A pointer starts at the sentinel and examines each successor, unlinking any node whose data matches the target.
class Solution:
def removeElements(self, head: Optional[ListNode], target: int) -> Optional[ListNode]:
sentinel = ListNode(0, head)
prev = sentinel
while prev.next is not None:
if prev.next.val == target:
prev.next = prev.next.next
else:
prev = prev.next
return sentinel.next
Reversing a singly linked list is most intuitive with two pointers. A previous pointer (None initially) and a current pointer (initially the head) move forward together, redirecting each node's next reference. Its critical to capture current.next before the link is overwritten.
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
previous = None
current = head
while current:
nxt = current.next
current.next = previous
previous = current
current = nxt
return previous
The same idea translates into a recursive helper that carries the accumulating reversed prefix. The base case returns the new head when the current node is exhausted.
class Solution:
def reverseListRec(self, head: Optional[ListNode]) -> Optional[ListNode]:
def helper(node, acc):
if not node:
return acc
nxt = node.next
node.next = acc
return helper(nxt, node)
return helper(head, None)
Implementing a custom linked list with index-based operations bneefits from a dummy head and a tracked size. When traversing to an index, the current reference must stop at the node before the point of insertion or deletion. Using a for loop over the index value makes the logic explicit and avoids off-by-one errors.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self._sentinel = ListNode()
self._size = 0
def get(self, idx: int) -> int:
if idx < 0 or idx >= self._size:
return -1
current = self._sentinel.next
for _ in range(idx):
current = current.next
return current.val
def addAtHead(self, val: int) -> None:
node = ListNode(val, self._sentinel.next)
self._sentinel.next = node
self._size += 1
def addAtTail(self, val: int) -> None:
cur = self._sentinel
while cur.next:
cur = cur.next
cur.next = ListNode(val)
self._size += 1
def addAtIndex(self, idx: int, val: int) -> None:
if idx < 0 or idx > self._size:
return
prev = self._sentinel
for _ in range(idx):
prev = prev.next
node = ListNode(val, prev.next)
prev.next = node
self._size += 1
def deleteAtIndex(self, idx: int) -> None:
if idx < 0 or idx >= self._size:
return
prev = self._sentinel
for _ in range(idx):
prev = prev.next
if prev.next:
prev.next = prev.next.next
self._size -= 1