Files
CSC111/assignments/A1/a1_linked_list.py
T
2022-01-21 15:01:51 -05:00

170 lines
4.8 KiB
Python

"""CSC111 Winter 2021 Assignment 1: Linked Lists
Do not change this file -- it will not be submitted, and we will supply our own copy
for grading purposes.
Copyright and Usage Information
===============================
This file is provided solely for the personal and private use of students
taking CSC111 at the University of Toronto St. George campus. All forms of
distribution of this code, whether as given or with any changes, are
expressly prohibited. For more information on copyright for CSC111 materials,
please consult our Course Syllabus.
This file is Copyright (c) 2021 David Liu and Isaac Waller.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Iterable, Optional
@dataclass
class _Node:
"""A node in a linked list.
Note that this is considered a "private class", one which is only meant
to be used in this module by the LinkedList class, but not by client code.
Instance Attributes:
- item: The data stored in this node.
- next: The next node in the list, if any.
"""
item: Any
next: Optional[_Node] = None # By default, this node does not link to any other node
class LinkedList:
"""A linked list implementation of the List ADT.
"""
# Private Instance Attributes:
# - _first: The first node in the linked list, or None if the list is empty.
_first: Optional[_Node]
def __init__(self, items: Iterable) -> None:
"""Initialize a new linked list containing the given items.
"""
self._first = None
for item in items:
self.append(item)
def to_list(self) -> list:
"""Return a built-in Python list containing the items of this linked list.
The items in this linked list appear in the same order in the returned list.
"""
items_so_far = []
curr = self._first
while curr is not None:
items_so_far.append(curr.item)
curr = curr.next
return items_so_far
def __len__(self) -> int:
"""Return the number of elements in this list.
>>> lst = LinkedList([])
>>> len(lst) # Equivalent to lst.__len__()
0
>>> lst = LinkedList([1, 2, 3])
>>> len(lst)
3
"""
curr = self._first
len_so_far = 0
while curr is not None:
len_so_far += 1
curr = curr.next
return len_so_far
def __contains__(self, item: Any) -> bool:
"""Return whether item is in this linked list.
"""
curr = self._first
while curr is not None:
if curr.item == item:
return True
curr = curr.next
return False
def __getitem__(self, i: int) -> Any:
"""Return the item stored at index i in this linked list.
Raise an IndexError if index i is out of bounds.
Preconditions:
- i >= 0
"""
curr = self._first
curr_index = 0
while curr is not None:
if curr_index == i:
return curr.item
curr = curr.next
curr_index = curr_index + 1
# If we've reached the end of the list and no item has been returned,
# the given index is out of bounds.
raise IndexError
def pop(self, i: int) -> Any:
"""Remove and return the item at index i.
Raise IndexError if i >= len(self).
Preconditions:
- i >= 0
>>> lst = LinkedList([1, 2, 10, 200])
>>> lst.pop(1)
2
>>> lst.to_list()
[1, 10, 200]
"""
if i == 0:
if self._first is None:
raise IndexError
else:
item = self._first.item
self._first = self._first.next
return item
else:
curr = self._first
curr_index = 0
while not (curr is None or curr_index == i - 1):
curr = curr.next
curr_index = curr_index + 1
if curr is None:
raise IndexError
else:
if curr.next is None:
raise IndexError
else:
item = curr.next.item
curr.next = curr.next.next
return item
def append(self, item: Any) -> None:
"""Add the given item to the end of this linked list.
"""
new_node = _Node(item)
if self._first is None:
self._first = new_node
else:
curr = self._first
while curr.next is not None:
curr = curr.next
# After the loop, curr is the last node in the LinkedList.
assert curr is not None and curr.next is None
curr.next = new_node