-
Notifications
You must be signed in to change notification settings - Fork 1
/
LC141-Linked-List-Cycle.py
129 lines (109 loc) · 3.67 KB
/
LC141-Linked-List-Cycle.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""
Given head, the head of a linked list, determine if the linked list
has a cycle in it.
There is a cycle in a linked list if there is some node in the list
that can be reached again by continuously following the next pointer.
Internally, pos is used to denote the index of the node that tail's
next pointer is connected to. Note that pos is not passed as a parameter.
Return true if there is a cycle in the linked list. Otherwise, return false.
Example 1:
Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where the tail connects
to the 1st node (0-indexed).
Example 2:
Input: head = [1,2], pos = 0
Output: true
Explanation: There is a cycle in the linked list, where the tail connects
to the 0th node.
Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.
Constraints:
(*) The number of the nodes in the list is in the range [0, 104].
(*) -10^5 <= Node.val <= 10^5
(*) pos is -1 or a valid index in the linked-list.
Follow up: Can you solve it using O(1) (i.e. constant) memory?
"""
from LinkedList import ListNode
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycleWithMemory(self, head: ListNode) -> bool:
"""
Find if the LinkedList has a cycle by assigning
visited attribute to it
:param head:
:return:
Runtime complexity: O(n)
Space complexity: O(n)
"""
while head is not None:
if hasattr(head, 'visited'):
return True
head.visited = True
head = head.next
return False
def hasCycleNoMemory(self, head) -> bool:
"""
Find if a LinkedList has a cycle. If it has, then there is n such that
2^n is greater than the length of the cycle. Therefore, moving along the
linked list for 2^n steps, we have to meet the original node.
Increase n until the node is met or until we reach None
:param head: head of the linked list
:return: true if the linked list has a cycle, false otherwise
Runtime complexity: O(n)
Space complexity: O(1)
"""
if head is None:
return False
n = 1
memo = id(head)
head = head.next
counter = 1 << n - 1
while head is not None:
if id(head) == memo:
return True
elif counter:
counter -= 1
else:
memo = id(head)
n += 1
counter = 1 << n
head = head.next
return False
if __name__ == '__main__':
from run_tests import run_tests
correct_answers = [
[[3,2,0,-4], 1, True],
[[1,2], 0, True],
[[1], -1, False],
[list(range(100)), 3, True],
[list(range(100)), -1, False]
]
for i in range(len(correct_answers)):
arr, pos, ans = correct_answers[i]
root = ListNode.to_linkedlist(arr)
if pos == 0:
node = root
while node.next is not None:
node = node.next
node.next = root
elif pos > 0:
node = root
pos -= 1
while node.next is not None:
if not pos:
loop_starts = node
node = node.next
pos -= 1
node.next = loop_starts
correct_answers[i] = [root, ans]
methods = ['hasCycleWithMemory', 'hasCycleNoMemory', ]
for method in methods:
print(f'Running tests for {method}')
run_tests(getattr(Solution(), method), correct_answers)