Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Core Linked List Operations: Removal, Design, and Reversal in Python

Tech May 13 3

Working with singly linked lists requires a firm grasp of pointer manipulation and edge-case handling. The following sections break down three classic problems: removing nodes by value, building a custom linked list with index-based operations, and reversing a list in-place.

Eliminating Nodes with a Specific Value

When tasked with deleting all nodes that hold a target value, the head itself may need removal. Since standard deletion relies on updating the previous node’s next reference, a sentinel node placed before the head simplifies the logic. Traverse the list using a ptr starting at the sentinel. If ptr.next carries the target value, bypass it; otherwise advance ptr. At the end, return sentinel.next as the new head.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        sentinel = ListNode(next=head)
        ptr = sentinel
        while ptr.next:
            if ptr.next.val == val:
                ptr.next = ptr.next.next
            else:
                ptr = ptr.next
        return sentinel.next

Building a Customizable Singly Linked List

Designing a linked list class involves maintaining a sentinel head and a size counter to support get, addAtHead, addAtTail, addAtIndex, and deleteAtIndex. All index-based operations must validate bounds before traversing.

Data Structure Setup

A nested Node class stores a value and a reference. The MyLinkedList constructor creates a sentinel node and initializes _size to zero.

class MyLinkedList:
    class Node:
        __slots__ = ('val', 'next')
        def __init__(self, val=0, next=None):
            self.val = val
            self.next = next

    def __init__(self):
        self._sentinel = self.Node()
        self._size = 0

Retrieval by Index

Return -1 for negative indices or indices ≥ _size. Start from the first real node (_sentinel.next) and advance index times.

    def get(self, index: int) -> int:
        if index < 0 or index >= self._size:
            return -1
        current = self._sentinel.next
        for _ in range(index):
            current = current.next
        return current.val

Insertion at Head and Tail

For head insertion, link the new node right after the sentinel and increment _size. For tail insertion, walk to the last node, then append.

    def addAtHead(self, val: int) -> None:
        self._sentinel.next = self.Node(val, self._sentinel.next)
        self._size += 1

    def addAtTail(self, val: int) -> None:
        cur = self._sentinel.next
        if not cur:          # list is empty
            self.addAtHead(val)
            return
        while cur.next:
            cur = cur.next
        cur.next = self.Node(val)
        self._size += 1

Insertion at a Specific Position

If index equals _size, the node goes to the tail; if index exceeds _size or is negative, do nothing. Otherwise, start from the sentinel and move index steps, then link the new node.

    def addAtIndex(self, index: int, val: int) -> None:
        if index > self._size or index < 0:
            return
        prev = self._sentinel
        for _ in range(index):
            prev = prev.next
        prev.next = self.Node(val, prev.next)
        self._size += 1

Deletion by Index

Guard against invalid indices. Position a pointer before the target and bypass it.

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self._size:
            return
        prev = self._sentinel
        for _ in range(index):
            prev = prev.next
        prev.next = prev.next.next
        self._size -= 1

Reversing a Linked List In-Place

The iterative two-pointer approach reverses the next pointers one by one. Initialize prev = None and curr = head. Inside a loop, cache curr.next, point curr.next to prev, shift both pointers forward (prev = curr, curr = saved). When the loop ends, prev becomes the new head.

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        prev = None
        curr = head
        while curr:
            nxt = curr.next
            curr.next = prev
            prev = curr
            curr = nxt
        return prev

This approach uses O(1) extra space and runs in O(n) time. A recursive alternative can also be defined, but the iterative version remains straightforward and widely used.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.