I'm following a tutorial on Single Linked List (SLL).
There are so many methods in the code, if you had time please feel free to review any part of it and I can work on it and repost it.
I'm especially interested in object-oriented, algorithms, design, logic, exception handling, and other programming side of it. Other developer-oriented issues such as variable namings, documenting, commenting, and such are not really a concern here since it's just for practicing algorithm and data structures, and have to follow the tutorial as much as possible.
Thanks so much for your time!
""" ππππππππππππππππ Single Linked List (SLL): A simple object-oriented implementation of Single Linked List (SLL) with some associated methods, such as create list, count nodes, delete nodes, and such. ππππππππππππππππ """ class Node: """ Instantiates a node """ def __init__(self, value): """ Node class constructor which sets the value and link of the node """ self.info = value self.link = None class SingleLinkedList: """ Instantiates the SLL class """ def __init__(self): """ SLL class constructor which sets the value and link of the node """ self.start = None def create_single_linked_list(self): """ Reads values from stdin and appends them to this list and creates a SLL with integer nodes """ try: number_of_nodes = int(input("π Enter a positive integer between 1-50 for the number of nodes you wish to have in the list: ")) if number_of_nodes <= 0 or number_of_nodes > 51: print("π The number of nodes though must be an integer between 1 to 50!") self.create_single_linked_list() except Exception as e: print("π Error: ", e) self.create_single_linked_list() try: for _ in range(number_of_nodes): try: data = int(input("π Enter an integer for the node to be inserted: ")) self.insert_node_at_end(data) except Exception as e: print("π Error: ", e) except Exception as e: print("π Error: ", e) def count_sll_nodes(self): """ Counts the nodes of the linked list """ try: p = self.start n = 0 while p is not None: n += 1 p = p.link if n >= 1: print(f"π The number of nodes in the linked list is {n}") else: print(f"π The SLL does not have a node!") except Exception as e: print("π Error: ", e) def search_sll_nodes(self, x): """ Searches the x integer in the linked list """ try: position = 1 p = self.start while p is not None: if p.info == x: print(f"π YAAAY! We found {x} at position {position}") return True #Increment the position position += 1 #Assign the next node to the current node p = p.link else: print(f"π Sorry! We couldn't find {x} at any position. Maybe, you might want to use option 9 and try again later!") return False except Exception as e: print("π Error: ", e) def display_sll(self): """ Displays the list """ try: if self.start is None: print("π Single linked list is empty!") return display_sll = "π Single linked list nodes are: " p = self.start while p is not None: display_sll += str(p.info) + "\t" p = p.link print(display_sll) except Exception as e: print("π Error: ", e) def insert_node_in_beginning(self, data): """ Inserts an integer in the beginning of the linked list """ try: temp = Node(data) temp.link = self.start self.start = temp except Exception as e: print("π Error: ", e) def insert_node_at_end(self, data): """ Inserts an integer at the end of the linked list """ try: temp = Node(data) if self.start is None: self.start = temp return p = self.start while p.link is not None: p = p.link p.link = temp except Exception as e: print("π Error: ", e) def insert_node_after_another(self, data, x): """ Inserts an integer after the x node """ try: p = self.start while p is not None: if p.info == x: break p = p.link if p is None: print(f"π Sorry! {x} is not in the list.") else: temp = Node(data) temp.link = p.link p.link = temp except Exception as e: print("π Error: ", e) def insert_node_before_another(self, data, x): """ Inserts an integer before the x node """ try: # If list is empty if self.start is None: print("π Sorry! The list is empty.") return # If x is the first node, and new node should be inserted before the first node if x == self.start.info: temp = Node(data) temp.link = p.link p.link = temp # Finding the reference to the prior node containing x p = self.start while p.link is not None: if p.link.info == x: break p = p.link if p.link is not None: print(f"π Sorry! {x} is not in the list.") else: temp = Node(data) temp.link = p.link p.link = temp except Exception as e: print("π Error: ", e) def insert_node_at_position(self, data, k): """ Inserts an integer in k position of the linked list """ try: # if we wish to insert at the first node if k == 1: temp = Node(data) temp.link = self.start self.start = temp return p = self.start i = 1 while i < k-1 and p is not None: p = p.link i += 1 if p is None: print(f"π The max position is {i}") else: temp = Node(data) temp.link = self.start self.start = temp except Exception as e: print("π Error: ", e) def delete_a_node(self, x): """ Deletes a node of a linked list """ try: # If list is empty if self.start is None: print("π Sorry! The list is empty.") return # If there is only one node if self.start.info == x: self.start = self.start.link # If more than one node exists p = self.start while p.link is not None: if p.link.info == x: break p = p.link if p.link is None: print(f"π Sorry! {x} is not in the list.") else: p.link = p.link.link except Exception as e: print("π Error: ", e) def delete_sll_first_node(self): """ Deletes the first node of a linked list """ try: if self.start is None: return self.start = self.start.link except Exception as e: print("π Error: ", e) def delete_sll_last_node(self): """ Deletes the last node of a linked list """ try: # If the list is empty if self.start is None: return # If there is only one node if self.start.link is None: self.start = None return # If there is more than one node p = self.start # Increment until we find the node prior to the last node while p.link.link is not None: p = p.link p.link = None except Exception as e: print("π Error: ", e) def reverse_sll(self): """ Reverses the linked list """ try: prev = None p = self.start while p is not None: next = p.link p.link = prev prev = p p = next self.start = prev except Exception as e: print("π Error: ", e) def bubble_sort_sll_nodes_data(self): """ Bubble sorts the linked list on integer values """ try: # If the list is empty or there is only one node if self.start is None or self.start.link is None: print("π The list has no or only one node and sorting is not required.") end = None while end != self.start.link: p = self.start while p.link != end: q = p.link if p.info > q.info: p.info, q.info = q.info, p.info p = p.link end = p except Exception as e: print("π Error: ", e) def bubble_sort_sll(self): """ Bubble sorts the linked list """ try: # If the list is empty or there is only one node if self.start is None or self.start.link is None: print("π The list has no or only one node and sorting is not required.") end = None while end != self.start.link: r = p = self.start while p.link != end: q = p.link if p.info > q.info: p.link = q.link q.link = p if p != self.start: r.link = q.link else: self.start = q p, q = q, p r = p p = p.link end = p except Exception as e: print("π Error: ", e) def sll_has_cycle(self): """ Tests the list for cycles using Tortoise and Hare Algorithm (Floyd's cycle detection algorithm) """ try: if self.find_sll_cycle() is None: return False else: return True except Exception as e: print("π Error: ", e) def find_sll_cycle(self): """ Finds cycles in the list, if any """ try: # If there is one node or none, there is no cycle if self.start is None or self.start.link is None: return None # Otherwise, slowR = self.start fastR = self.start while slowR is not None and fastR is not None: slowR = slowR.link fastR = fastR.link.link if slowR == fastR: return slowR return None except Exception as e: print("π Error: ", e) def remove_cycle_from_sll(self): """ Removes the cycles """ try: c = self.find_sll_cycle() # If there is no cycle if c is None: return print(f"π There is a cycle at node: ", c.info) p = c q = c len_cycles = 0 while True: len_cycles += 1 q = q.link if p == q: break print(f"π The cycle length is {len_cycles}") len_rem_list = 0 p = self.start while p != q: len_rem_list += 1 p = p.link q = q.link print(f"π The number of nodes not included in the cycle is {len_rem_list}") length_list = len_rem_list + len_cycles print(f"π The SLL length is {length_list}") # This for loop goes to the end of the SLL, and set the last node to None and the cycle is removed. p = self.start for _ in range(length_list-1): p = p.link p.link = None except Exception as e: print("π Error: ", e) def insert_cycle_in_sll(self, x): """ Inserts a cycle at a node that contains x """ try: if self.start is None: return False p = self.start px = None prev = None while p is not None: if p.info == x: px = p prev = p p = p.link if px is not None: prev.link = px else: print(f"π Sorry! {x} is not in the list.") except Exception as e: print("π Error: ", e) def merge_using_new_list(self, list2): """ Merges two already sorted SLLs by creating new lists """ merge_list = SingleLinkedList() merge_list.start = self._merge_using_new_list(self.start, list2.start) return merge_list def _merge_using_new_list(self, p1, p2): """ Private method of merge_using_new_list """ if p1.info <= p2.info: Start_merge = Node(p1.info) p1 = p1.link else: Start_merge = Node(p2.info) p2 = p2.link pM = Start_merge while p1 is not None and p2 is not None: if p1.info <= p2.info: pM.link = Node(p1.info) p1 = p1.link else: pM.link = Node(p2.info) p2 = p2.link pM = pM.link #If the second list is finished, yet the first list has some nodes while p1 is not None: pM.link = Node(p1.info) p1 = p1.link pM = pM.link #If the second list is finished, yet the first list has some nodes while p2 is not None: pM.link = Node(p2.info) p2 = p2.link pM = pM.link return Start_merge def merge_inplace(self, list2): """ Merges two already sorted SLLs in place in O(1) of space """ merge_list = SingleLinkedList() merge_list.start = self._merge_inplace(self.start, list2.start) return merge_list def _merge_inplace(self, p1, p2): """ Merges two already sorted SLLs in place in O(1) of space """ if p1.info <= p2.info: Start_merge = p1 p1 = p1.link else: Start_merge = p2 p2 = p2.link pM = Start_merge while p1 is not None and p2 is not None: if p1.info <= p2.info: pM.link = p1 pM = pM.link p1 = p1.link else: pM.link = p2 pM = pM.link p2 = p2.link if p1 is None: pM.link = p2 else: pM.link = p1 return Start_merge def merge_sort_sll(self): """ Sorts the linked list using merge sort algorithm """ self.start = self._merge_sort_recursive(self.start) def _merge_sort_recursive(self, list_start): """ Recursively calls the merge sort algorithm for two divided lists """ # If the list is empty or has only one node if list_start is None or list_start.link is None: return list_start # If the list has two nodes or more start_one = list_start start_two = self._divide_list(self_start) start_one = self._merge_sort_recursive(start_one) start_two = self._merge_sort_recursive(start_two) start_merge = self._merge_inplace(start_one, start_two) return start_merge def _divide_list(self, p): """ Divides the linked list into two almost equally sized lists """ # Refers to the third nodes of the list q = p.link.link while q is not None and p is not None: # Increments p one node at the time p = p.link # Increments q two nodes at the time q = q.link.link start_two = p.link p.link = None return start_two def concat_second_list_to_sll(self, list2): """ Concatenates another SLL to an existing SLL """ # If the second SLL has no node if list2.start is None: return # If the original SLL has no node if self.start is None: self.start = list2.start return # Otherwise traverse the original SLL p = self.start while p.link is not None: p = p.link # Link the last node to the first node of the second SLL p.link = list2.start def test_merge_using_new_list_and_inplace(self): """ """ LIST_ONE = SingleLinkedList() LIST_TWO = SingleLinkedList() LIST_ONE.create_single_linked_list() LIST_TWO.create_single_linked_list() print("1οΈβ£ The unsorted first list is: ") LIST_ONE.display_sll() print("2οΈβ£ The unsorted second list is: ") LIST_TWO.display_sll() LIST_ONE.bubble_sort_sll_nodes_data() LIST_TWO.bubble_sort_sll_nodes_data() print("1οΈβ£ The sorted first list is: ") LIST_ONE.display_sll() print("2οΈβ£ The sorted second list is: ") LIST_TWO.display_sll() LIST_THREE = LIST_ONE.merge_using_new_list(LIST_TWO) print("The merged list by creating a new list is: ") LIST_THREE.display_sll() LIST_FOUR = LIST_ONE.merge_inplace(LIST_TWO) print("The in-place merged list is: ") LIST_FOUR.display_sll() def test_all_methods(self): """ Tests all methods of the SLL class """ OPTIONS_HELP = """ πππππππππππππππππππππππππππππππππππππππππ Select a method from 1-19: πππππππππππππππππππππππππππππππππππππππππ βΉοΈ (1) π Create a single liked list (SLL). βΉοΈ (2) π Display the SLL. βΉοΈ (3) π Count the nodes of SLL. βΉοΈ (4) π Search the SLL. βΉοΈ (5) π Insert a node at the beginning of the SLL. βΉοΈ (6) π Insert a node at the end of the SLL. βΉοΈ (7) π Insert a node after a specified node of the SLL. βΉοΈ (8) π Insert a node before a specified node of the SLL. βΉοΈ (9) π Delete the first node of SLL. βΉοΈ (10) π Delete the last node of the SLL. βΉοΈ (11) π Delete a node you wish to remove. βΉοΈ (12) π Reverse the SLL. βΉοΈ (13) π Bubble sort the SLL by only exchanging the integer values. βΉοΈ (14) π Bubble sort the SLL by exchanging links. βΉοΈ (15) π Merge sort the SLL. βΉοΈ (16) π Insert a cycle in the SLL. βΉοΈ (17) π Detect if the SLL has a cycle. βΉοΈ (18) π Remove cycle in the SLL. βΉοΈ (19) π Test merging two bubble-sorted SLLs. βΉοΈ (20) π Concatenate a second list to the SLL. βΉοΈ (21) π Exit. πππππππππππππππππππππππππππππππππππππππππ """ self.create_single_linked_list() while True: print(OPTIONS_HELP) UI_OPTION = int(input("π Enter an integer for the method you wish to run using the above help: ")) if UI_OPTION == 1: data = int(input("π Enter an integer to be inserted at the end of the list: ")) x = int(input("π Enter an integer to be inserted after that: ")) self.insert_node_after_another(data, x) elif UI_OPTION == 2: self.display_sll() elif UI_OPTION == 3: self.count_sll_nodes() elif UI_OPTION == 4: data = int(input("π Enter an integer to be searched: ")) self.search_sll_nodes(data) elif UI_OPTION == 5: data = int(input("π Enter an integer to be inserted at the beginning: ")) self.insert_node_in_beginning(data) elif UI_OPTION == 6: data = int(input("π Enter an integer to be inserted at the end: ")) self.insert_node_at_end(data) elif UI_OPTION == 7: data = int(input("π Enter an integer to be inserted: ")) x = int(input("π Enter an integer to be inserted before that: ")) self.insert_node_before_another(data, x) elif UI_OPTION == 8: data = int(input("π Enter an integer for the node to be inserted: ")) k = int(input("π Enter an integer for the position at which you wish to insert the node: ")) self.insert_node_before_another(data, k) elif UI_OPTION == 9: self.delete_sll_first_node() elif UI_OPTION == 10: self.delete_sll_last_node() elif UI_OPTION == 11: data = int(input("π Enter an integer for the node you wish to remove: ")) self.delete_a_node(data) elif UI_OPTION == 12: self.reverse_sll() elif UI_OPTION == 13: self.bubble_sort_sll_nodes_data() elif UI_OPTION == 14: self.bubble_sort_sll() elif UI_OPTION == 15: self.merge_sort_sll() elif UI_OPTION == 16: data = int(input("π Enter an integer at which a cycle has to be formed: ")) self.insert_cycle_in_sll(data) elif UI_OPTION == 17: if self.sll_has_cycle(): print("π The linked list has a cycle. ") else: print("π YAAAY! The linked list does not have a cycle. ") elif UI_OPTION == 18: self.remove_cycle_from_sll() elif UI_OPTION == 19: self.test_merge_using_new_list_and_inplace() elif UI_OPTION == 20: list2 = self.create_single_linked_list() self.concat_second_list_to_sll(list2) elif UI_OPTION == 21: break else: print("π Option must be an integer, between 1 to 21.") print() if __name__ == '__main__': # Instantiates a new SLL object SLL_OBJECT = SingleLinkedList() SLL_OBJECT.test_all_methods()