From 18f1dcd48a22d3c84a8839d2198ed35e68d3641a Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Fri, 25 Sep 2020 22:09:29 +0800 Subject: [PATCH] Updated singly_linked_list (#2477) * Updated singly_linked_list * fixup! Format Python code with psf/black push * undo __repr__ * updating DIRECTORY.md * UNTESTED CHANGES: Add an .__iter__() method. This will break tests, etc. * fixup! Format Python code with psf/black push * len(tuple(iter(self))) * fixed __repr__() * Update data_structures/linked_list/singly_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/singly_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/singly_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/singly_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/singly_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/singly_linked_list.py Co-authored-by: Christian Clauss * Update data_structures/linked_list/singly_linked_list.py Co-authored-by: Christian Clauss Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 2 + .../linked_list/singly_linked_list.py | 277 ++++++++++-------- 2 files changed, 157 insertions(+), 122 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 03044e010..f08c31794 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -25,7 +25,9 @@ * [Sum Of Subsets](https://github.com/TheAlgorithms/Python/blob/master/backtracking/sum_of_subsets.py) ## Bit Manipulation + * [Binary And Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_and_operator.py) * [Binary Or Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_or_operator.py) + * [Binary Xor Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_xor_operator.py) ## Blockchain * [Chinese Remainder Theorem](https://github.com/TheAlgorithms/Python/blob/master/blockchain/chinese_remainder_theorem.py) diff --git a/data_structures/linked_list/singly_linked_list.py b/data_structures/linked_list/singly_linked_list.py index 1f3e03a31..39b14c520 100644 --- a/data_structures/linked_list/singly_linked_list.py +++ b/data_structures/linked_list/singly_linked_list.py @@ -9,105 +9,15 @@ class Node: class LinkedList: def __init__(self): - self.head = None # initialize head to None + self.head = None - def insert_tail(self, data) -> None: - if self.head is None: - self.insert_head(data) # if this is first node, call insert_head - else: - temp = self.head - while temp.next: # traverse to last node - temp = temp.next - temp.next = Node(data) # create node & link to tail + def __iter__(self): + node = self.head + while node: + yield node.data + node = node.next - def insert_head(self, data) -> None: - new_node = Node(data) # create a new node - if self.head: - new_node.next = self.head # link new_node to head - self.head = new_node # make NewNode as head - - def print_list(self) -> None: # print every node data - temp = self.head - while temp: - print(temp.data) - temp = temp.next - - def delete_head(self): # delete from head - temp = self.head - if self.head: - self.head = self.head.next - temp.next = None - return temp - - def delete_tail(self): # delete from tail - temp = self.head - if self.head: - if self.head.next is None: # if head is the only Node in the Linked List - self.head = None - else: - while temp.next.next: # find the 2nd last element - temp = temp.next - # (2nd last element).next = None and temp = last element - temp.next, temp = None, temp.next - return temp - - def is_empty(self) -> bool: - return self.head is None # return True if head is none - - def reverse(self): - prev = None - current = self.head - - while current: - # Store the current node's next node. - next_node = current.next - # Make the current node's next point backwards - current.next = prev - # Make the previous node be the current node - prev = current - # Make the current node the next node (to progress iteration) - current = next_node - # Return prev in order to put the head at the end - self.head = prev - - def __repr__(self): # String representation/visualization of a Linked Lists - current = self.head - string_repr = "" - while current: - string_repr += f"{current} --> " - current = current.next - # END represents end of the LinkedList - return string_repr + "END" - - # Indexing Support. Used to get a node at particular position - def __getitem__(self, index): - current = self.head - - # If LinkedList is empty - if current is None: - raise IndexError("The Linked List is empty") - - # Move Forward 'index' times - for _ in range(index): - # If the LinkedList ends before reaching specified node - if current.next is None: - raise IndexError("Index out of range.") - current = current.next - return current - - # Used to change the data of a particular node - def __setitem__(self, index, data): - current = self.head - # If list is empty - if current is None: - raise IndexError("The Linked List is empty") - for i in range(index): - if current.next is None: - raise IndexError("Index out of range.") - current = current.next - current.data = data - - def __len__(self): + def __len__(self) -> int: """ Return length of linked list i.e. number of nodes >>> linked_list = LinkedList() @@ -126,45 +36,168 @@ class LinkedList: >>> len(linked_list) 0 """ - if not self.head: - return 0 + return len(tuple(iter(self))) - count = 0 - cur_node = self.head - while cur_node.next: - count += 1 - cur_node = cur_node.next - return count + 1 + def __repr__(self): + """ + String representation/visualization of a Linked Lists + """ + return "->".join([str(item) for item in self]) + + def __getitem__(self, index): + """ + Indexing Support. Used to get a node at particular position + """ + if index < 0: + raise ValueError("Negative indexes are not yet supported") + for i, node in enumerate(self): + if i == index: + return node.data + + # Used to change the data of a particular node + def __setitem__(self, index, data): + current = self.head + # If list is empty + if current is None: + raise IndexError("The Linked List is empty") + for i in range(index): + if current.next is None: + raise IndexError("list index out of range") + current = current.next + current.data = data + + def insert_tail(self, data) -> None: + self.insert_nth(len(self), data) + + def insert_head(self, data) -> None: + self.insert_nth(0, data) + + def insert_nth(self, index: int, data) -> None: + if not 0 <= index <= len(self): + raise IndexError("list index out of range") + new_node = Node(data) + if self.head is None: + self.head = new_node + elif index == 0: + new_node.next = self.head # link new_node to head + self.head = new_node + else: + temp = self.head + for _ in range(index - 1): + temp = temp.next + new_node.next = temp.next + temp.next = new_node + + def print_list(self) -> None: # print every node data + print(self) + + def delete_head(self): + return self.delete_nth(0) + + def delete_tail(self): # delete from tail + return self.delete_nth(len(self) - 1) + + def delete_nth(self, index: int = 0): + if not 0 <= index <= len(self) - 1: # test if index is valid + raise IndexError("list index out of range") + delete_node = self.head # default first node + if index == 0: + self.head = self.head.next + else: + temp = self.head + for _ in range(index - 1): + temp = temp.next + delete_node = temp.next + temp.next = temp.next.next + return delete_node.data + + def is_empty(self) -> bool: + return self.head is None + + def reverse(self): + prev = None + current = self.head + + while current: + # Store the current node's next node. + next_node = current.next + # Make the current node's next point backwards + current.next = prev + # Make the previous node be the current node + prev = current + # Make the current node the next node (to progress iteration) + current = next_node + # Return prev in order to put the head at the end + self.head = prev + + +def test_singly_linked_list() -> None: + """ + >>> test_singly_linked_list() + """ + linked_list = LinkedList() + assert linked_list.is_empty() is True + assert str(linked_list) == "" + + try: + linked_list.delete_head() + assert False # This should not happen. + except IndexError: + assert True # This should happen. + + try: + linked_list.delete_tail() + assert False # This should not happen. + except IndexError: + assert True # This should happen. + + for i in range(10): + assert len(linked_list) == i + linked_list.insert_nth(i, i + 1) + assert str(linked_list) == "->".join(str(i) for i in range(1, 11)) + + linked_list.insert_head(0) + linked_list.insert_tail(11) + assert str(linked_list) == "->".join(str(i) for i in range(0, 12)) + + assert linked_list.delete_head() == 0 + assert linked_list.delete_nth(9) == 10 + assert linked_list.delete_tail() == 11 + assert str(linked_list) == "->".join(str(i) for i in range(1, 10)) def main(): - A = LinkedList() - A.insert_head(input("Inserting 1st at head ").strip()) - A.insert_head(input("Inserting 2nd at head ").strip()) + from doctest import testmod + + testmod() + + linked_list = LinkedList() + linked_list.insert_head(input("Inserting 1st at head ").strip()) + linked_list.insert_head(input("Inserting 2nd at head ").strip()) print("\nPrint list:") - A.print_list() - A.insert_tail(input("\nInserting 1st at tail ").strip()) - A.insert_tail(input("Inserting 2nd at tail ").strip()) + linked_list.print_list() + linked_list.insert_tail(input("\nInserting 1st at tail ").strip()) + linked_list.insert_tail(input("Inserting 2nd at tail ").strip()) print("\nPrint list:") - A.print_list() + linked_list.print_list() print("\nDelete head") - A.delete_head() + linked_list.delete_head() print("Delete tail") - A.delete_tail() + linked_list.delete_tail() print("\nPrint list:") - A.print_list() + linked_list.print_list() print("\nReverse linked list") - A.reverse() + linked_list.reverse() print("\nPrint list:") - A.print_list() + linked_list.print_list() print("\nString representation of linked list:") - print(A) + print(linked_list) print("\nReading/changing Node data using indexing:") - print(f"Element at Position 1: {A[1]}") - A[1] = input("Enter New Value: ").strip() + print(f"Element at Position 1: {linked_list[1]}") + linked_list[1] = input("Enter New Value: ").strip() print("New list:") - print(A) - print(f"length of A is : {len(A)}") + print(linked_list) + print(f"length of linked_list is : {len(linked_list)}") if __name__ == "__main__":