Home > database >  remove kth node from end of linked list
remove kth node from end of linked list

Time:05-18

I am trying to remove the kth element from the END of a linked list. Here is my code

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


def removeKthNodeFromEnd(head, k):
    if k == 0 or head is None:
        return

    temp = head
    while temp is not None and k > 1:
        temp = temp.next
        k -= 1

    if temp is None or k > 1:
        return head

    trailing = None
    leading = head
    while temp.next is not None:
        trailing = leading
        leading = leading.next
        temp = temp.next

    if trailing is None:
        head = head.next
    else:
        trailing.next = leading.next


head = LinkedList(0)
head.next = LinkedList(1)
head.next.next = LinkedList(2)
head.next.next.next = LinkedList(3)
head.next.next.next.next = LinkedList(4)
head.next.next.next.next.next = LinkedList(5)
head.next.next.next.next.next.next = LinkedList(6)
head.next.next.next.next.next.next.next = LinkedList(7)
head.next.next.next.next.next.next.next.next = LinkedList(8)
head.next.next.next.next.next.next.next.next.next = LinkedList(9)


removeKthNodeFromEnd(head, 10)

while head is not None:
    print(head.value)
    head = head.next

But this does not work and prints out all the values in the linked list from 0 to 9. Why is this the case? If the node to delete is the head node which I check by checking trailing is None, then I update head = head.next. I am changing head to be the next node. I can return head after this update and the result of head = removeKthNodeFromEnd(head, k) will give me the desired output but why can't I do it the other way without returning anything? For instance in my solution I can get rid of any element in between the first and last node including the last and works just fine. The original head gets updated and node gets removed. But when trying to update the head node head = head.next it does not work.

One way I achieved this is by doing the following...

if trailing is None:
  head.value = head.next.value
  head.next = head.next.next

But why must I use the values? To me this seems the same as

if trailing is None:
  head = head.next
  head.next = head.next.next

but does not work

CodePudding user response:

Consider utilizing a dummy node to simplify your logic:

from typing import Optional


class LinkedList:

  def __init__(self, value: int):
    self.value = value
    self.next = None

  def __iter__(self):
    return LinkedListIterator(self)


class LinkedListIterator:

  def __init__(self, head: LinkedList):
    self.curr = head

  def __iter__(self):
    return self

  def __next__(self):
    if not self.curr:
      raise StopIteration
    else:
      value = self.curr.value
      self.curr = self.curr.next
      return f'[{value}]'


def remove_kth_node_from_end(head: Optional[LinkedList],
                             k: int) -> Optional[LinkedList]:
  """Removes the kth node end of the passed in LinkedList."""
  dummy = trailing = leading = LinkedList(0)
  dummy.next = head
  for _ in range(k):
    leading = leading.next
  while leading and leading.next:
    leading = leading.next
    trailing = trailing.next
  trailing.next = trailing.next.next
  return dummy.next


def main() -> None:
  head = LinkedList(0)
  head.next = LinkedList(1)
  head.next.next = LinkedList(2)
  head.next.next.next = LinkedList(3)
  head.next.next.next.next = LinkedList(4)
  head.next.next.next.next.next = LinkedList(5)
  head.next.next.next.next.next.next = LinkedList(6)
  head.next.next.next.next.next.next.next = LinkedList(7)
  head.next.next.next.next.next.next.next.next = LinkedList(8)
  head.next.next.next.next.next.next.next.next.next = LinkedList(9)
  k = 3
  print(f'{k = }')
  print('Before removing kth node from end:')
  print('->'.join(head))
  new_head = remove_kth_node_from_end(head, k)
  print('After removing kth node from end:')
  print('->'.join(new_head))


if __name__ == '__main__':
  main()

Output:

k = 3
Before removing kth node from end:
[0]->[1]->[2]->[3]->[4]->[5]->[6]->[7]->[8]->[9]
After removing kth node from end:
[0]->[1]->[2]->[3]->[4]->[5]->[6]->[8]->[9]

CodePudding user response:

It doesn't work because head in that function is a local name. Assigning to a local variable never does anything to other variables, even not when they happen to have the same name (like the global head).

The "trick" you have by moving a linked list value with head.value = head.next.value will not work when the list only has one node.

One way to do this, is to return the (potentially modified) value of head, and expect the caller to assign this back to their own head variable.

So:

  • in removeKthNodeFromEnd make sure all return statements to return head, and that you add a return head at the very end. In short, removeKthNodeFromEnd should always return head.

  • in the main program, change the call to head = removeKthNodeFromEnd(head, 10)

That will solve your issue.

Here are some ideas to make your code more elegant:

  • Create a separate class for maintaining the head of the linked list
  • Improve the constructor, so that it is easier to initialise a linked list
  • Add an iterator so it is easier to print a linked list
class Node:  # renamed
    def __init__(self, value, nxt=None):
        self.value = value
        self.next = nxt  # is now an argument


class LinkedList:  # added
    def __init__(self, *values):
        self.head = None
        if values:  # populate the list with values
            for value in reversed(values):
                self.push_front(value)

    def push_front(self, value):
        self.head = Node(value, self.head)

    def __iter__(self):
        node = self.head
        while node:
            yield node.value
            node = node.next
    
    def removeKthNodeFromEnd(self, k):
        if k <= 0:
            return
        temp = self.head
        for _ in range(k):
            if not temp:  # k is out of range
                return
            temp = temp.next

        if temp:
            trailing = self.head
            while temp.next:
                trailing = trailing.next
                temp = temp.next
            trailing.next = trailing.next.next
        else:
            self.head = self.head.next
    

lst = LinkedList(*range(10))
print(*lst)
lst.removeKthNodeFromEnd(0)
print(*lst)
  • Related