In Place Reversal of a Linked List - kjingers/Grokking-Notes GitHub Wiki

Reverse a Linked List

def reverse(head):
  
  prev = None

  while head:
    next = head.next
    head.next = prev
    prev = head
    head = next
  return prev

Reverse a Sublist

First need to skip to position p (skip p-1 Nodes). Keep track of the end of the first part, node at position p (becomes end of sublist).

Then, reverse nodes from position p to position q (q - p + 1 nodes). After reversal, current becomes the beginning of second part, and prev becomes to beginning of the sublist.

Lastly, connect the first part and second part with the sublist accordingly.

For related questions, we can call this function multiple times with different values of p and q.

# p and q are positions
def reverse_sub_list(head, p, q):

  # Cannot Reverse a sublist with length <= 1
  if q <= p:
    return head

  current = head
  prev = None
  i = 0

  # Skip First p-1 Nodes
  while current is not None and i < (p - 1):
    prev = current
    current = current.next
    i += 1

  # Save End of the first part and Beginning of sublist, 
  # which will become the end
  end_of_first_part = prev
  end_of_sublist = current


  # Reverse the sublist of length (q - p) + 1
  i = 0
  next = None

  while current is not None and i < ((q - p) + 1):
    next = current.next
    current.next = prev
    prev = current
    current = next
    i += 1
  
  # Prev is now the beginning of sublist
  # Current is the first node after the sublist
  beginning_of_sublist = prev
  beginning_of_second_part = current

  # Set end_of_sublist's next to the next part
  end_of_sublist.next = beginning_of_second_part

  # If end_of_first_part is None, then the reversal starts with
  # the head (p = 1)
  if end_of_first_part is not None:
    end_of_first_part.next = beginning_of_sublist
  else:
    head = beginning_of_sublist
  
  return head

Reverse every K-element Sub-List

Similar to above. Except we loop forever and break when current is None. Have to keep track of end_of_previous and end_of_sublist each loop so that we can make the appropriate links after reversing sublist.

def reverse_every_k_elements(head, k):
  
  if k <= 1 or head is None:
    return head
  
  previous = None
  current = head

  # Loop Forver (until we break)
  while True:
    # Save off last node of previous section
    # and end of current sub-list (once reversed)
    head.print_list()
    last_of_previous = previous
    last_of_sublist = current

    i = 0

    # Reverse the sublist of length k
    while current is not None and i < k:
      next = current.next
      current.next = previous
      previous = current
      current = next
      i += 1
    
    # Connect Previous Section to Sublist
    # If Sublist started with head, then set head
    if last_of_previous is not None:
      last_of_previous.next = previous
    else:
      head = previous

    # Connect end of sublist to next section
    last_of_sublist.next = current

    # If no more nodes (current loop ended at None)
    if current is None:
      break

    # Set previous for next loop
    previous = last_of_sublist

  return head

Reverse Alternating k-element Sublists

Very similar to above, except after reversing, skip k nodes. Also, makes more sense to use while current is not None instead of while True

def reverse_alternate_k_elements(head, k):
  if head is None or k <= 1:
    return head

  previous = None
  current = head

  while current is not None:
    # Store Last Node of Previous Section
    # Store Last Node of Sublist
    last_of_previous = previous
    last_of_sublist = current

    i = 0
    # Reverse k nodes
    while current is not None and i < k:
      next = current.next
      current.next = previous
      previous = current
      current = next
      i += 1

    # Connect previous section to sublist
    if last_of_previous is None:
      head = previous
    else:
      last_of_previous.next = previous
    
    # Connect sublist to next section
    last_of_sublist.next = current

    i = 0
    # Skip k nodes
    while current is not None and i < k:
      previous = current
      current = current.next
      i += 1

  return head

Rotate a Linked List

A little different than other reversing problems. Instead, we just have to skip length - rotations nodes, and set the head and last_node accordingly.

def rotate(head, rotations):
  
  if head is None or head.next is None or rotations < 1:
    return head

  last_node = head
  list_length = 1
  # Get length of list
  while last_node.next is not None:
    last_node = last_node.next
    list_length += 1

  # No need to do more than list_length rotations
  rotations %= list_length

  # Go ahead connect last node to head to make circular list
  last_node.next = head

  last_of_rotated = head
  skip_length = list_length - rotations
  # Now need to get new last node and new head
  for i in range(skip_length - 1):
    last_of_rotated = last_of_rotated.next

  # Set the new head
  # Set the last skipped node's next to None
  head = last_of_rotated.next
  last_of_rotated.next = None

  return head