Remove nth node from a linked list end
Firstly, the logic of the removeNthFromEnd function can be split into these parts:


calculate length of list
calculate index \$i\$ from the beginning of the list, based on B
remove \$i\$th element from the beginning




1. Calculating the length

def list_len(head):
  length = 0
  while head:
    head = head.next
    length += 1
  return length


2. Calculating index from the beginning of the list

i = list_len(A) - B


3. Removing \$i\$th element

def remove_ith(head, i):

  if i == 0:
    return head.next

  lst = head
  for _ in range(i-1):
    lst = lst.next

  lst.next = lst.next.next

  return head


So your removeNthFromEnd function will look like this:

def remove_nth_from_end(head, n):
  length = list_len(head)
  i = length - n

  if not (0 &lt;= i &lt; length):
    return head

  return remove_ith(head, i)


I took the liberty and renamed removeNthFromEnd to obey PEP8, a Python style guide, according to which function names should be in lower_snake_case.

I also renamed parameters from A, B to head, n. Descriptive names with some meaning are always better than just some random letter, even if it's more typing. However sometimes a letter is enough, such as in case of n or i. Especially if the function name is removeNthFromEnd – one would expect such function takes an argument n.



This basic solution has the same performance disadvantage as your solution – in the worst case, you traverse the list twice.

That's not very efficient, at least not time-wise. So I did a little optimization, which in this case might be considered cheating: I convert the linked list into a list of nodes. That way the linked list is only traversed once and you have constant-time access to all nodes. The disadvantage is of course increased memory usage, which is \$O(n)\$ compared to \$O(1)\$ of your solution, where \$n\$ is the length of the list. Optimally you would use a doubly-linked list. That way you would traverse the list at most once (on average only half of the list would be traversed), and the auxiliary space complexity would be constant, as with your original solution.

def remove_nth_from_end_(head, n):

  as_list = []
  node = head

  # Convert linked list to "array".
  while node:
    as_list.append(node)
    node = node.next

  # Calculate index from beginning.
  i = len(as_list) - n

  # Check that the list is not empty (in which case nothing can be removed)
  # and the index is within the bounds of the list.
  if as_list and i in range(len(as_list)):

    if i == 0:
      # Removing first element – just shift the head.
      head = head.next

    else:
      # Set next-pointer of the previous element to the next element.
      # This is where the constant-time access happens.
      as_list[i-1].next = as_list[i].next

  return head


To further optimize the space complexity, you could only keep the last n+1 elements while traversing the list. That would reduce the auxiliary space complexity to \$O(i_{e})\$, \$i_{e}\$ being the index of the removed element based from the end of the list.