From 511dd6a9db69864b9d388e5038edb5835089b241 Mon Sep 17 00:00:00 2001 From: Benjamin Adovasio Date: Sun, 15 Mar 2026 16:24:38 -0400 Subject: [PATCH] copied from project 4 to project 5 --- .vscode/settings.json | 3 + Project 5/LinkedList.py | 229 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 Project 5/LinkedList.py diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c9ebf2d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:system" +} \ No newline at end of file diff --git a/Project 5/LinkedList.py b/Project 5/LinkedList.py new file mode 100644 index 0000000..b1f04d5 --- /dev/null +++ b/Project 5/LinkedList.py @@ -0,0 +1,229 @@ +""" +This file is duplicated from my Project 4 repo and is identical +""" + +###################################### +# 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) # create a new node new_node + new_node.set_next(self.head) # set the new node's next pointer to the current head of the list + self.head = new_node # update the head pointer to point to the new node + self.size += 1 # increase the size of the list by 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 ## + """ + The insert_tail method creates a new node with the given value and adds it to the end of the linked list. If the list is empty, it sets the head pointer to the new node. Otherwise, it traverses the list until it reaches the last node and updates its next pointer to point to the new node. Finally, it increments the size of the list by 1. + """ + new_node = Node(value) # create a new node new_node + + if self.head is None: + self.head = new_node #if the list is empty, set the head pointer to the new node + else: #else go through list until we reach end and set the last node's next pointer to the new node + current = self.head + while current.get_next() is not None: + current = current.get_next() + current.set_next(new_node) + + self.size += 1 #increase the size of the list by 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 # store the current head node in a variable removed_node + self.head = self.head.get_next() # update the head pointer to point to the next node in the list (which could be None if there was only one node) + + self.size -= 1 #decrease the size of the list by 1 + return removed_node.get_data() # return the data item from the removed node + + 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 the list has only one node, we can simply remove the head and return its data. Otherwise, we need to traverse the list to find the second-to-last node, update its next pointer to None, and return the data from the last node. + """ + if self.head.get_next() is None: #continue if the list has only one node + removed_data = self.head.get_data() #return the data + self.head = None #set the head to none + self.size -= 1 #decrease the size of the list by 1 + return removed_data #return the data from the removed node + + current = self.head #start from the head node + + while current.get_next().get_next() is not None: #while the next node's next pointer is not None, keep going + current = current.get_next() #move current to the next node + + removed_data = current.get_next().get_data() + current.set_next(None) + + self.size -= 1 #decrease the size of the list by 1 + return removed_data #return the data from the removed node + + + 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() \ No newline at end of file