From 378e932781c528e671ab1f5654699c7c68d9c409 Mon Sep 17 00:00:00 2001 From: kanthuc Date: Wed, 16 Sep 2020 16:55:45 -0700 Subject: [PATCH 1/8] added an algorithm which checks a linked list for loops and returns true if one is found --- data_structures/linked_list/contains_loop.py | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 data_structures/linked_list/contains_loop.py diff --git a/data_structures/linked_list/contains_loop.py b/data_structures/linked_list/contains_loop.py new file mode 100644 index 000000000000..f86fc201e159 --- /dev/null +++ b/data_structures/linked_list/contains_loop.py @@ -0,0 +1,50 @@ +from typing import Any + + +class Node: + """ + Class to represent a single node in a singly linked list. + """ + + def __init__(self, data: Any) -> None: + self.data = data + self.next_node = None + + +def contains_loop(root: Node) -> bool: + """ + given a node, returns true if linked list contains a loop + returns false otherwise + """ + + counter1 = root + counter2 = root + + while counter1.next_node and counter2.next_node and counter2.next_node.next_node: + counter1 = counter1.next_node + counter2 = counter2.next_node.next_node + + if counter1 is counter2: + return True + + return False + + +if __name__ == "__main__": + node1 = Node(1) + node1.next_node = Node(2) + node1.next_node.next_node = Node(3) + node1.next_node.next_node.next_node = Node(4) + node1.next_node.next_node.next_node = node1.next_node + print(contains_loop(node1)) + + node2 = Node(5) + node2.next_node = Node(6) + node2.next_node.next_node = Node(7) + node2.next_node.next_node.next_node = Node(8) + print(contains_loop(node2)) + + node3 = Node(1) + # node3.next_node = Node(2) + # node3.next_node.next_node = node3 + print(contains_loop(node3)) From d37fd6e706abdd5c3c0729aeeb6058c6ea876738 Mon Sep 17 00:00:00 2001 From: kanthuc Date: Thu, 17 Sep 2020 17:49:29 -0700 Subject: [PATCH 2/8] added doctests and clarified meaning of loop --- data_structures/linked_list/contains_loop.py | 23 +++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/data_structures/linked_list/contains_loop.py b/data_structures/linked_list/contains_loop.py index f86fc201e159..00bb27eec6cb 100644 --- a/data_structures/linked_list/contains_loop.py +++ b/data_structures/linked_list/contains_loop.py @@ -13,8 +13,25 @@ def __init__(self, data: Any) -> None: def contains_loop(root: Node) -> bool: """ - given a node, returns true if linked list contains a loop + A linked list contains a loop when traversing through a loop, no null is reached. + + Given a node, returns true if linked list contains a loop returns false otherwise + + >>> node1 = Node(1) + >>> node1.next_node = Node(2) + >>> node1.next_node.next_node = Node(3) + >>> node1.next_node.next_node.next_node = Node(4) + >>> node1.next_node.next_node.next_node = node1.next_node + >>> contains_loop(node1) + True + + >>> node2 = Node(1) + >>> node2.next_node = Node(2) + >>> node2.next_node.next_node = Node(1) + >>> node2.next_node.next_node.next_node = Node(2) + >>> contains_loop(node2) + False """ counter1 = root @@ -40,8 +57,8 @@ def contains_loop(root: Node) -> bool: node2 = Node(5) node2.next_node = Node(6) - node2.next_node.next_node = Node(7) - node2.next_node.next_node.next_node = Node(8) + node2.next_node.next_node = Node(5) + node2.next_node.next_node.next_node = Node(6) print(contains_loop(node2)) node3 = Node(1) From 92578b02a30cd0cfc6bd9095210fb3064499e51e Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 18 Sep 2020 21:56:21 +0200 Subject: [PATCH 3/8] Define Node.__iter__() --- data_structures/linked_list/contains_loop.py | 67 -------------------- data_structures/linked_list/has_loop.py | 51 +++++++++++++++ 2 files changed, 51 insertions(+), 67 deletions(-) delete mode 100644 data_structures/linked_list/contains_loop.py create mode 100644 data_structures/linked_list/has_loop.py diff --git a/data_structures/linked_list/contains_loop.py b/data_structures/linked_list/contains_loop.py deleted file mode 100644 index 00bb27eec6cb..000000000000 --- a/data_structures/linked_list/contains_loop.py +++ /dev/null @@ -1,67 +0,0 @@ -from typing import Any - - -class Node: - """ - Class to represent a single node in a singly linked list. - """ - - def __init__(self, data: Any) -> None: - self.data = data - self.next_node = None - - -def contains_loop(root: Node) -> bool: - """ - A linked list contains a loop when traversing through a loop, no null is reached. - - Given a node, returns true if linked list contains a loop - returns false otherwise - - >>> node1 = Node(1) - >>> node1.next_node = Node(2) - >>> node1.next_node.next_node = Node(3) - >>> node1.next_node.next_node.next_node = Node(4) - >>> node1.next_node.next_node.next_node = node1.next_node - >>> contains_loop(node1) - True - - >>> node2 = Node(1) - >>> node2.next_node = Node(2) - >>> node2.next_node.next_node = Node(1) - >>> node2.next_node.next_node.next_node = Node(2) - >>> contains_loop(node2) - False - """ - - counter1 = root - counter2 = root - - while counter1.next_node and counter2.next_node and counter2.next_node.next_node: - counter1 = counter1.next_node - counter2 = counter2.next_node.next_node - - if counter1 is counter2: - return True - - return False - - -if __name__ == "__main__": - node1 = Node(1) - node1.next_node = Node(2) - node1.next_node.next_node = Node(3) - node1.next_node.next_node.next_node = Node(4) - node1.next_node.next_node.next_node = node1.next_node - print(contains_loop(node1)) - - node2 = Node(5) - node2.next_node = Node(6) - node2.next_node.next_node = Node(5) - node2.next_node.next_node.next_node = Node(6) - print(contains_loop(node2)) - - node3 = Node(1) - # node3.next_node = Node(2) - # node3.next_node.next_node = node3 - print(contains_loop(node3)) diff --git a/data_structures/linked_list/has_loop.py b/data_structures/linked_list/has_loop.py new file mode 100644 index 000000000000..458019d30bb5 --- /dev/null +++ b/data_structures/linked_list/has_loop.py @@ -0,0 +1,51 @@ +from typing import Any + + +class Node: + def __init__(self, data: Any) -> None: + self.data = data + self.next_node = None + + def __iter__(self): + node = self + while node: + yield node.data + node = node.next_node + + def has_loop(self) -> bool: + >>> node1 = Node(1) + >>> node1.next_node = Node(2) + >>> node1.next_node.next_node = Node(3) + >>> node1.next_node.next_node.next_node = Node(4) + >>> node1.next_node.next_node.next_node = node1.next_node + >>> has_loop(node1) + True + + >>> node2 = Node(1) + >>> node2.next_node = Node(2) + >>> node2.next_node.next_node = Node(1) + >>> node2.next_node.next_node.next_node = Node(2) + >>> contains_loop(node2) + False + """ + return len(list(self)) != len(set(self)) + + +if __name__ == "__main__": + node1 = Node(1) + node1.next_node = Node(2) + node1.next_node.next_node = Node(3) + node1.next_node.next_node.next_node = Node(4) + node1.next_node.next_node.next_node = node1.next_node + print(has_loop(node1)) + + node2 = Node(5) + node2.next_node = Node(6) + node2.next_node.next_node = Node(5) + node2.next_node.next_node.next_node = Node(6) + print(has_loop(node2)) + + node3 = Node(1) + # node3.next_node = Node(2) + # node3.next_node.next_node = node3 + print(has_loop(node3)) From 519f71a05e2fd30afa71280befbd4108c4842678 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 18 Sep 2020 22:08:37 +0200 Subject: [PATCH 4/8] Update and rename has_loop.py to has_duplicate_data.py --- .../{has_loop.py => has_duplicate_data.py} | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) rename data_structures/linked_list/{has_loop.py => has_duplicate_data.py} (69%) diff --git a/data_structures/linked_list/has_loop.py b/data_structures/linked_list/has_duplicate_data.py similarity index 69% rename from data_structures/linked_list/has_loop.py rename to data_structures/linked_list/has_duplicate_data.py index 458019d30bb5..18b42430e2ac 100644 --- a/data_structures/linked_list/has_loop.py +++ b/data_structures/linked_list/has_duplicate_data.py @@ -5,28 +5,24 @@ class Node: def __init__(self, data: Any) -> None: self.data = data self.next_node = None - + def __iter__(self): node = self while node: yield node.data node = node.next_node - - def has_loop(self) -> bool: + + @property + def has_duplicate_data(self) -> bool: >>> node1 = Node(1) >>> node1.next_node = Node(2) >>> node1.next_node.next_node = Node(3) >>> node1.next_node.next_node.next_node = Node(4) + >>> node.has_duplicate_data + False >>> node1.next_node.next_node.next_node = node1.next_node - >>> has_loop(node1) + >>> node.has_duplicate_data True - - >>> node2 = Node(1) - >>> node2.next_node = Node(2) - >>> node2.next_node.next_node = Node(1) - >>> node2.next_node.next_node.next_node = Node(2) - >>> contains_loop(node2) - False """ return len(list(self)) != len(set(self)) @@ -37,15 +33,13 @@ def has_loop(self) -> bool: node1.next_node.next_node = Node(3) node1.next_node.next_node.next_node = Node(4) node1.next_node.next_node.next_node = node1.next_node - print(has_loop(node1)) + print(node1.has_duplicate_data) node2 = Node(5) node2.next_node = Node(6) node2.next_node.next_node = Node(5) node2.next_node.next_node.next_node = Node(6) - print(has_loop(node2)) + print(node2.has_duplicate_data) node3 = Node(1) - # node3.next_node = Node(2) - # node3.next_node.next_node = node3 - print(has_loop(node3)) + print(node3.has_duplicate_data) From 444b3cec89295d233d18c5925b49e2f3fff309ec Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 18 Sep 2020 22:11:50 +0200 Subject: [PATCH 5/8] Update has_duplicate_data.py --- data_structures/linked_list/has_duplicate_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/data_structures/linked_list/has_duplicate_data.py b/data_structures/linked_list/has_duplicate_data.py index 18b42430e2ac..88511ff0188c 100644 --- a/data_structures/linked_list/has_duplicate_data.py +++ b/data_structures/linked_list/has_duplicate_data.py @@ -14,6 +14,7 @@ def __iter__(self): @property def has_duplicate_data(self) -> bool: + """ >>> node1 = Node(1) >>> node1.next_node = Node(2) >>> node1.next_node.next_node = Node(3) From cbd04a6ef37d46eb4111766cf54eed730c26e315 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 18 Sep 2020 22:20:43 +0200 Subject: [PATCH 6/8] Update has_duplicate_data.py --- .../linked_list/has_duplicate_data.py | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/data_structures/linked_list/has_duplicate_data.py b/data_structures/linked_list/has_duplicate_data.py index 88511ff0188c..455cc86b08c6 100644 --- a/data_structures/linked_list/has_duplicate_data.py +++ b/data_structures/linked_list/has_duplicate_data.py @@ -15,32 +15,33 @@ def __iter__(self): @property def has_duplicate_data(self) -> bool: """ - >>> node1 = Node(1) - >>> node1.next_node = Node(2) - >>> node1.next_node.next_node = Node(3) - >>> node1.next_node.next_node.next_node = Node(4) - >>> node.has_duplicate_data + >>> root_node = Node(1) + >>> root_node.next_node = Node(2) + >>> root_node.next_node.next_node = Node(3) + >>> root_node.next_node.next_node.next_node = Node(4) + >>> root_node.has_duplicate_data False - >>> node1.next_node.next_node.next_node = node1.next_node - >>> node.has_duplicate_data + >>> root_node.next_node.next_node.next_node = root_node.next_node + >>> root_node.has_duplicate_data True """ return len(list(self)) != len(set(self)) if __name__ == "__main__": - node1 = Node(1) - node1.next_node = Node(2) - node1.next_node.next_node = Node(3) - node1.next_node.next_node.next_node = Node(4) - node1.next_node.next_node.next_node = node1.next_node - print(node1.has_duplicate_data) - - node2 = Node(5) - node2.next_node = Node(6) - node2.next_node.next_node = Node(5) - node2.next_node.next_node.next_node = Node(6) - print(node2.has_duplicate_data) - - node3 = Node(1) - print(node3.has_duplicate_data) + root_node = Node(1) + root_node.next_node = Node(2) + root_node.next_node.next_node = Node(3) + root_node.next_node.next_node.next_node = Node(4) + print(root_node.has_duplicate_data) # False + root_node.next_node.next_node.next_node = root_node.next_node + print(root_node.has_duplicate_data) # True + + root_node = Node(5) + root_node.next_node = Node(6) + root_node.next_node.next_node = Node(5) + root_node.next_node.next_node.next_node = Node(6) + print(root_node.has_duplicate_data) # True + + root_node = Node(1) + print(root_node.has_duplicate_data) # False From 448091d40fe1089d5e1ae6cea18775f97d6cb1e2 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 18 Sep 2020 22:42:37 +0200 Subject: [PATCH 7/8] Update and rename has_duplicate_data.py to has_loop.py --- .../{has_duplicate_data.py => has_loop.py} | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) rename data_structures/linked_list/{has_duplicate_data.py => has_loop.py} (62%) diff --git a/data_structures/linked_list/has_duplicate_data.py b/data_structures/linked_list/has_loop.py similarity index 62% rename from data_structures/linked_list/has_duplicate_data.py rename to data_structures/linked_list/has_loop.py index 455cc86b08c6..88524c5e8765 100644 --- a/data_structures/linked_list/has_duplicate_data.py +++ b/data_structures/linked_list/has_loop.py @@ -1,5 +1,8 @@ from typing import Any +class ContainsLoopError(Exception): + pass + class Node: def __init__(self, data: Any) -> None: @@ -8,24 +11,33 @@ def __init__(self, data: Any) -> None: def __iter__(self): node = self + visited = [] while node: + if node in visited: + raise ContainsLoopError + visited.append(node) yield node.data node = node.next_node @property - def has_duplicate_data(self) -> bool: + def has_loop(self) -> bool: """ + A loop is when the exact same Node appears more than once in a linked list. >>> root_node = Node(1) >>> root_node.next_node = Node(2) >>> root_node.next_node.next_node = Node(3) >>> root_node.next_node.next_node.next_node = Node(4) - >>> root_node.has_duplicate_data + >>> root_node.has_loop False >>> root_node.next_node.next_node.next_node = root_node.next_node - >>> root_node.has_duplicate_data + >>> root_node.has_loop True """ - return len(list(self)) != len(set(self)) + try: + list(self) + return False + except ContainsLoopError: + return True if __name__ == "__main__": @@ -33,15 +45,15 @@ def has_duplicate_data(self) -> bool: root_node.next_node = Node(2) root_node.next_node.next_node = Node(3) root_node.next_node.next_node.next_node = Node(4) - print(root_node.has_duplicate_data) # False + print(root_node.has_loop) # False root_node.next_node.next_node.next_node = root_node.next_node - print(root_node.has_duplicate_data) # True + print(root_node.has_loop) # True root_node = Node(5) root_node.next_node = Node(6) root_node.next_node.next_node = Node(5) root_node.next_node.next_node.next_node = Node(6) - print(root_node.has_duplicate_data) # True + print(root_node.has_loop) # False root_node = Node(1) - print(root_node.has_duplicate_data) # False + print(root_node.has_loop) # False From 9a41f51fb45f543144de239fc25581db5317d297 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 18 Sep 2020 22:45:40 +0200 Subject: [PATCH 8/8] Update has_loop.py --- data_structures/linked_list/has_loop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/data_structures/linked_list/has_loop.py b/data_structures/linked_list/has_loop.py index 88524c5e8765..405ece7e27c8 100644 --- a/data_structures/linked_list/has_loop.py +++ b/data_structures/linked_list/has_loop.py @@ -1,5 +1,6 @@ from typing import Any + class ContainsLoopError(Exception): pass