-
Notifications
You must be signed in to change notification settings - Fork 0
/
missing_number.py
141 lines (117 loc) · 4.75 KB
/
missing_number.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
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env python3
# Missing Number
#
# https://leetcode.com/problems/missing-number
#
# Given an array nums containing n distinct numbers in the range [0, n], return
# the only number in the range that is missing from the array.
from typing import List
def test():
"""
Run `pytest <this-file>`.
"""
def test_algo(algo):
assert algo(nums=[3, 0, 1]) == 2
assert algo(nums=[0, 1]) == 2
assert algo(nums=[9, 6, 4, 2, 3, 5, 7, 0, 1]) == 8
assert algo(nums=[1, 2]) == 0
# Test all different algorithms/implementations
solution = Solution()
for algo in [
solution.brute_force,
solution.maths,
solution.maths_optimal,
solution.difference_of_sets,
solution.binary_search,
]:
test_algo(algo)
class Solution:
def brute_force(self, nums: List[int]) -> int:
"""
Approach: Save numbers as hashset.
Idea: Get the element range (min to max elements), and check if one is missing.
Time: O(n): Getting the max element takes O(n), and then we search for a missing element from 0 to the max element, n, taking O(n).
Space: O(n): We store the numbers input lists as a set.
Leetcode: 106 ms runtime, 18.50 MB memory
"""
n = len(nums)
nums_set = set(nums)
for i in range(0, n + 1):
if i not in nums_set:
return i
raise Exception("unreachable")
def difference_of_sets(self, nums: List[int]) -> int:
"""
Approach: Difference of sets.
Idea: Turn both [0, n] and the input numbers into sets, and their difference will be the missing element.
Time: O(n): Turning both into sets takes O(n) each, and taking the difference is assumed to take O(n) as well.
Space: O(n): Two sets with length O(n) are created.
Leetcode: 113 ms runtime, 18.94 MB memory
"""
n = len(nums)
return (set(range(0, n + 1)) - set(nums)).pop()
def maths(self, nums: List[int]) -> int:
"""
Approach: Sum range and numbers.
Idea: We can find the missing number by subtracting the sum of the numbers from the sum of [0, n].
Time: O(n): Summing [0, n] takes O(n), and summing the input numbers takes O(n).
Space: O(1): No additional memory is used.
Leetcode: 104 ms runtime, 17.74 MB memory
"""
# missing number = sum([0, n]) - sum(nums)
n = len(nums)
return sum(range(0, n + 1)) - sum(nums)
def maths_optimal(self, nums: List[int]) -> int:
"""
Approach: Sum range and numbers.
Idea: We can find the missing number by subtracting the sum of the numbers from the sum of [0, n].
Time: O(n): Summing [0, n] takes O(1) using the formula for the arithmetic series, and summing the input numbers takes O(n).
Space: O(1): No additional memory is used.
Leetcode: 108 ms runtime, 17.76 MB memory
"""
def sum_first_n_even(n: int) -> int:
"""
Return the sum of the first 1 to n (both inclusive).
Assume n is even.
"""
# Arithmetic series:
# sum(1 to n)
# = 1 + 2 + ... + (n-1) + n
# = (1 + n) + (2 + (n-1)) + ...
# = (1 + n) + (1 + n) + ...
# = (n/2)(1 + n)
return (n // 2) * (1 + n)
def sum_first_n(n: int) -> int:
if (n % 2) == 0:
return sum_first_n_even(n)
else:
return sum_first_n_even(n - 1) + n
# missing number = sum([0, n]) - sum(nums)
n = len(nums)
return sum_first_n(n) - sum(nums)
def binary_search(self, nums: List[int]) -> int:
"""
Approach: Sort and binary search
Idea: Sort the numbers, so we can binary search for the missing number.
Time: O(n log n): Sorting takes O(n log n), and then we do a binary search taking O(log n).
Space: O(1): No additional memory is used.
Leetcode: 121 ms runtime, 17.68 MB memory
"""
n = len(nums)
nums_sorted = sorted(nums)
(l, r) = (0, n - 1)
while l < r:
m = (l + r) // 2
if nums_sorted[m] == m:
# For all k < m, nums_sorted[k] == nums_sorted[m] => search right.
l = m + 1
elif nums_sorted[m] < m:
# There must be some nums_sorted[k], k < m, missing => search left.
r = m
elif nums_sorted[m] > m:
# There must be some nums_sorted[k], k < m, missing => search left.
r = m
if l == nums_sorted[l]:
return l + 1
else:
return l