-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathlargest-range.py
129 lines (120 loc) · 3.97 KB
/
largest-range.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
# Largest Range
# 🔴 Hard
#
# https://www.algoexpert.io/questions/largest-range
#
# Tags: Array - Hashmap - Two Pointers
import timeit
# The obvious solution would be to sort the input and remove duplicates,
# then iterate over the sorted input counting the length of contiguous
# sequences.
#
# Time complexity: O(n*log(n)) - Sorting has the highest complexity.
# Space complexity: O(n) - Sorting in Python takes up to n/2 memory.
class SortingSolution:
def largestRange(self, array):
# Write your code here.
array = sorted(set(array))
res = [array[0], array[0]]
current = [array[0], array[0]]
for i in range(1, len(array)):
if array[i] == array[i - 1] + 1:
current[1] = array[i]
else:
if current[1] - current[0] > res[1] - res[0]:
res = current
current = [array[i], array[i]]
if current[1] - current[0] > res[1] - res[0]:
res = current
return res
# We can use a hash set and a two pointer approach, create a set of all
# numbers in the input, then start iterating over all the contiguous
# ranges that we can find removing them from the set, to do it, first we
# pop one value from the set and use two pointers to travel left and
# right until we arrive at a gap in the contiguous values, when we have
# computed the current range, check if it is the largest that we have
# seen and move onto the next.
#
# Time complexity: O(n) - We iterate twice over all elements in the
# input doing constant work. If we knew the max value of any element in
# the input, we could improve the algorithm using an array instead of
# the hashmap, saving the cost of hashing.
# Space complexity: O(n) - The hashmap.
class LinearSolution:
def largestRange(self, array):
# Register numbers that we have.
have = {num for num in array}
res = [array[0], array[0]]
# Iterate while we haven't visited all ranges.
while have:
# Pop any random element and visit all the contiguous
# elements removing them from the set.
num = have.pop()
# Initialize two pointers.
l = r = num
# Move left while that value was in the input.
while l - 1 in have:
l -= 1
have.remove(l)
# Move right while that value was in the input.
while r + 1 in have:
r += 1
have.remove(r)
# If this range is greater than the previous best, update it.
if r - l > res[1] - res[0]:
res = [l, r]
return res
def test():
executors = [
SortingSolution,
LinearSolution,
]
tests = [
[[1, 1, 1, 3, 4], [3, 4]],
[[8, 4, 2, 10, 3, 6, 7, 9, 1], [6, 10]],
[
[
19,
-1,
18,
17,
2,
10,
3,
12,
5,
16,
4,
11,
8,
7,
6,
15,
12,
12,
2,
1,
6,
13,
14,
],
[10, 19],
],
]
for executor in executors:
start = timeit.default_timer()
for _ in range(1):
for col, t in enumerate(tests):
sol = executor()
result = sol.largestRange(t[0])
exp = t[1]
assert result == exp, (
f"\033[93m» {result} <> {exp}\033[91m for"
+ f" test {col} using \033[1m{executor.__name__}"
)
stop = timeit.default_timer()
used = str(round(stop - start, 5))
cols = "{0:20}{1:10}{2:10}"
res = cols.format(executor.__name__, used, "seconds")
print(f"\033[92m» {res}\033[0m")
test()