Linked list quick reference
# Data types (attributes only)
@dataclass
class _Node:
item: Any
next: Optional[_Node] = None
class LinkedList:
_first: Optional[_Node]
# Linked list traveral pattern
curr = self._first
while curr is not None:
... curr.item ...
curr = curr.nextThe built-in list data type has another type of
deletion: list.remove, which removes the first occurrence
of an item. This is known as value-based deletion, since the
item that gets removed is based on its value rather than its index.
We will now implement an equivalent LinkedList.remove
method:
class LinkedList:
def remove(self, item: Any) -> None:
"""Remove the first occurence of item from the list.
Raise ValueError if the item is not found in the list.
>>> lst = LinkedList([10, 20, 30, 20])
>>> lst.remove(20)
>>> lst.to_list()
[10, 30, 20]
"""Suppose you want to remove 71 from the linked list
below.

Modify the diagram above to show how the linked list would change
when 71 is removed.
Which node needed to be mutated?
Here is the same linked list. Suppose we now wanted to remove
109. Modify the diagram to show how the linked list needs
to change.

Using these ideas, implement the LinkedList.remove
method.
Suppose we have a Python list of length 1,000,000,
and a LinkedList of length 1,000,000.
If we want to access the item at index 500,000 in each
list, would it be significantly faster for the list, the
LinkedList, or about the same amount of time for both?
Explain.
If we want to delete the item at index 0 in each list,
would it be significantly faster for the list, the
LinkedList, or about the same amount of time for both?
Explain.
If we want to insert an item at index 500,000 in each
list, would it be significantly faster for the list, the
LinkedList, or about the same amount of time for both?
Explain.
Implement a new method LinkedList.remove_all, which
is similar to LinkedList.remove but removes all
occurrences of the given item from the list (rather than just the first
occurrence).
Consider our current implementation of
LinkedList.__init__, which uses the append
method:
class LinkedList:
def __init__(self, items: Iterable) -> None:
self._first = None
for item in items:
self.append(item)What is the running time of LinkedList.append, in
terms of n, the length of the linked list?
Analyse the running time of this implementation of
LinkedList.__init__, in terms of \(m\), the length of items.
(Note that when the initializer is called, the length of
self is 0: the running time only depends
on items.)
Remember that when taking into account the running time of a helper function, you can drop the “Theta”, e.g. count a \(\Theta(n)\) function as \(n\) steps.
Comment: in this week’s tutorial, you’ll explore how to
implement a more efficient LinkedList initializer, but if
you’re working on this exercise before your tutorial, please feel free
start thinking about it now!