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


def _get_sum(node: Node) -> int:
    result = 0
    curr = node

    while curr:
        result += curr.val
        curr = curr.next

    return result


def get_sum_rec(node: Node) -> int:
    if not node:
        return 0

    return node.val + get_sum_rec(node.next)


class NumberLinkedList:
    head: Node = None

    def get_sum(self):
        return _get_sum(self.head)

    def add(self, num: int):
        if not self.head:
            self.head = Node(num)

            return self

        curr = self.head

        while curr.next:
            curr = curr.next

        curr.next = Node(num)

        return self

    def delete(self, num: int):
        if not self.head:
            raise ValueError(f"Number {num} not in the list")

        curr = self.head
        prev = None

        while curr:
            if curr.val == num:
                # first node
                if not prev:
                    self.head = curr.next
                    return self

                prev.next = curr.next

                return self

            prev = curr
            curr = curr.next

        raise ValueError(f"Number {num} not in the list")

    def to_arr(self):
        if not self.head:
            return []

        curr = self.head

        items = []

        while curr:
            items.append(curr.val)
            curr = curr.next

        return items

    def __str__(self):
        if not self.head:
            return ""

        curr = self.head
        s = f"{curr.val}"
        curr = curr.next

        while curr:
            s += f"-> {curr.val}"
            curr = curr.next

if __name__ == '__main__':
    l = NumberLinkedList()
    l.add(10).add(4).add(13).add(1)

    assert 28 == l.get_sum()
    assert 28 == get_sum_rec(l.head)

    assert l.to_arr() == [10, 4, 13, 1]
    assert l.delete(4).to_arr() == [10, 13, 1]

Linked list with sentinel nodes

It is a linked list with maintaining nodes at the head and tail of the linked list. Sentinel nodes are also present in empty linked list. They are however not part of the list

For this purpose we will use a DoublyNodes containing pointers to next and previous node as well

class DoublyNode:
    def __init__(self, val):
        self.val = val
        self.next = None
        self.prev = None


class SentinelLinkedList:
    def __init__(self):
        self.head = DoublyNode(None)
        self.tail = DoublyNode(None)

        self.head.next = self.tail
        self.tail.next = self.head

    def remove_left(self):
        if self.head == self.tail:
            return

        to_remove = self.head.next
        self.head.next = to_remove.next
        to_remove.next.prev = self.head

    def remove_right(self):
        if self.head == self.tail:
            return

        to_remove = self.tail.prev
        self.tail.prev = to_remove.prev
        to_remove.prev.next = self.tail

    def peek_left(self):
        return self.head.val

    def peek_right(self):
        return self .tail.val

Example Problems

def find_middle(head: Node):
    slow = fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

    return slow.val


def has_cycle(head: Node):
    slow = fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

        if slow == fast:
            return True

    return False


def has_cycle_hash(head: Node):
    seen = set()

    current = head
    while current:
        if current in seen:
            return True
        seen.add(current)
        current = current.next

    return False


def get_node(head: Node, k: int) -> int:
    slow = fast = head
    counter = 0

    while fast.next and counter < k:
        fast = fast.next
        counter += 1

    if counter != k:
        raise ValueError(f'Out of bounds, found {counter} elements')

    while fast:
        slow = slow.next
        fast = fast.next

    return slow.val


def deduplicate(head: Node) -> Node:
    if not head:
        return head

    prev = head
    curr = head.next

    while curr:
        if prev.val == curr.val:
            prev.next = curr.next
            curr = curr.next
        else:
            curr = curr.next
            prev = prev.next

    return head