Files
DCS229/Project 4/LinkedList.py
2026-03-13 07:52:53 -04:00

220 lines
6.1 KiB
Python

######################################
# DCS 229 -- Linked List
# Linked List implementation
# Date: 03/13/2026
# Name: Benjamin Adovasio
# Resources Used: https://docs.python.org/3/tutorial/datastructures.html
##########################################
from __future__ import annotations
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
# Want to define our own custom Exception class...
class EmptyError(Exception):
def __init__(self, message: str) -> None:
super().__init__(message)
self.message = message
class Node[T]:
''' class to implement a single node object in a singly-linked
linked list '''
def __init__(self, data: T):
self.data = data
self.next = None # points to another Node object
def get_data(self) -> T:
return self.data
def set_data(self, value: T) -> None:
self.data = value
def get_next(self) -> Node[T]:
return self.next
def set_next(self, next_node: Node[T]) -> None:
self.next = next_node
class LinkedList[T]:
''' class to implement a singly-linked linked list '''
def __init__(self) -> None:
self.head = None # the head pointer in the linked list
self.size = 0
def __len__(self) -> int:
''' returns the number of nodes in the linked list
Returns:
int - representing the number of nodes in the list
'''
return self.size
def insert_head(self, value: T) -> None:
''' adds the given T-type data value to the front of the linked list
Parameters:
value: a type T data item to be included as the data in the inserted Node
Returns:
nothing
'''
## YOUR CODE HERE ##
new_node = Node(value)
new_node.set_next(self.head)
self.head = new_node
self.size += 1
def insert_tail(self, value: T) -> None:
''' adds the given T-type data value to the end of the linked list
Parameters:
value: a type T data item to be included as the data in the inserted Node
Returns:
nothing
'''
## YOUR CODE HERE ##
new_node = Node(value)
if self.head is None:
self.head = new_node
else:
current = self.head
while current.get_next() is not None:
current = current.get_next()
current.set_next(new_node)
self.size += 1
def remove_head(self) -> T:
''' removes the first Node in the linked list, returning the data item
inside that Node... Remember to handle the special case of an
empty list (what should the head pointers be in that case?)
and remember to update the head pointer when appropriate.
Returns:
a T type data item extracted from the removed Node
Raises:
EmptyError exception if list is empty
'''
# raise an error if list is empty
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
if self.size == 0:
raise EmptyError("Cannot remove from an empy list")
## YOUR CODE HERE ##
removed_node = self.head
self.head = self.head.get_next()
self.size -= 1
return removed_node.get_data()
def remove_tail(self) -> T:
''' removes the last Node in the linked list, returning the data item
inside that Node... Remember to handle the special case of an
empty list
Returns:
a T type data item extracted from the removed Node
Raises:
EmptyError exception if list is empty
'''
# raise an error if list is empty
if self.size == 0:
raise EmptyError("Cannot remove from an empy list")
## YOUR CODE HERE ##
if self.head.get_next() is None:
removed_data = self.head.get_data()
self.head = None
self.size -= 1
return removed_data
current = self.head
while current.get_next().get_next() is not None:
current = current.get_next()
removed_data = current.get_next().get_data()
current.set_next(None)
self.size -= 1
return removed_data
def __str__(self):
''' returns a str representation of the linked list data
Returns:
an str representation of the linked list, showing head pointer
and data tiems
'''
str_ = "head->"
# start out at the head Node, and walk through Node by Node until we
# reach the end of the linked list (i.e., the ._next entry is None)
ptr_ = self.head
while ptr_ is not None:
str_ += "[" + str(ptr_.get_data()) + "]->"
ptr_ = ptr_.get_next() # move ptr_ to the next Node in the linked list
if self.head != None:
str_ = str_[:-2] # remove trailing "->"
str_ += "<-tail"
return str_
def main():
# create a LinkedList and try out some various adds and removes
ll = LinkedList()
try:
ll.remove_head()
except EmptyError as err:
print(err)
ll.insert_head(8)
assert len(ll) == 1
removed = ll.remove_head()
assert removed == 8
assert len(ll) == 0
ll.insert_tail(6)
ll.insert_tail(7)
ll.insert_tail(5)
print(ll)
assert len(ll) == 3
removed = ll.remove_tail()
assert removed == 5
assert len(ll) == 2
# ADD MORE TESTS
# test inserting multiple at head
ll.insert_head(1)
ll.insert_head(2)
ll.insert_head(3)
assert len(ll) == 5
print(ll)
# test removing head repeatedly
assert ll.remove_head() == 3
assert ll.remove_head() == 2
assert len(ll) == 3
# test removing tail until empty
assert ll.remove_tail() == 7
assert ll.remove_tail() == 6
assert ll.remove_tail() == 1
assert len(ll) == 0
# test removing from empty again
try:
ll.remove_tail()
except EmptyError:
print("Correctly caught empty list error")
if __name__ == "__main__":
main()