-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsearch-in-rotated-sorted-array.py
145 lines (132 loc) · 4.83 KB
/
search-in-rotated-sorted-array.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
142
143
144
145
# 33. Search in Rotated Sorted Array
# 🟠 Medium
#
# https://leetcode.com/problems/search-in-rotated-sorted-array/
#
# Tags: Array - Binary Search
import timeit
from typing import List
# First check if the array is actually rotated, if rotated, use Binary
# search to find the point at which the array is rotated, check which
# section could contain the target, then binary search that section.
#
# Time complexity: O(log(n)) - 2 binary searches with O(log(n)) each.
# Space complexity: O(1)
#
# Runtime: 70 ms, faster than 41.42%
# Memory Usage: 14.3 MB, less than 56.29%
class FindPivot:
def search(self, nums: List[int], target: int) -> int:
# For small inputs, do linear search.
if len(nums) < 10:
for i, num in enumerate(nums):
if num == target:
return i
return -1
# If the array is rotated
if nums[0] > nums[-1]:
# Search the index of the last element before the rotated
# elements.
left, right = 0, len(nums) - 1
while right > left + 1:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < nums[0]:
right = mid - 1
else:
left = mid
# The first element of the original array is to the right
# of left/right, check whether the target could be in the
# head or the tail and set the boundaries for the binary
# search.
if target < nums[0]:
left, right = right + 1, len(nums) - 1
elif target > nums[0]:
left, right = 0, right
else:
return 0 if nums[0] == target else -1
# The array is not rotated, do binary search on nums.
else:
left, right = 0, len(nums) - 1
# Binary search the section of the array that could contain the
# target.
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif target < nums[mid]:
right = mid - 1
else:
left = mid + 1
return -1
# Do binary search on the rotated array but consider the value of idx 0
# when considering how to update the pointers.
#
# Time complexity: O(log(n)) - We are doing binary search directly on
# the input.
# Space complexity: O(1)
#
# Runtime: 81 ms, faster than 19.64%
# Memory Usage: 14.1 MB, less than 91.15%
class CheckSection:
def search(self, nums: List[int], target: int) -> int:
# No edge cases to consider if we check left <= right.
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
# Check if mid is target.
if nums[mid] == target:
return mid
# If the section between left and mid does not overlap the
# intersection of the array.
if nums[left] <= nums[mid]:
# Take care of the case where the intersection could be
# to the right of mid.
if target > nums[mid] or target < nums[left]:
left = mid + 1
else:
right = mid - 1
# The intersection is not between left and mid, it could be
# to the left of left or to the right of mid.
else:
if target < nums[mid] or target > nums[right]:
right = mid - 1
else:
left = mid + 1
return -1
def test():
executors = [
FindPivot,
CheckSection,
]
tests = [
[[3, 1], 0, -1],
[[3, 1], 1, 1],
[[4, 5, 6, 7, 0, 1, 2], 6, 2],
[[40, 50, 60, 70, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 4],
[[40, 50, 60, 70, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 7, 11],
[[40, 50, 60, 70, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9, 13],
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 0],
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 3, 3],
[[4, 5, 6, 7, 0, 1, 2], 0, 4],
[[4, 5, 6, 7, 0, 1, 2], 3, -1],
[[1], 0, -1],
]
for executor in executors:
start = timeit.default_timer()
for _ in range(int(float("1"))):
for i, t in enumerate(tests):
sol = executor()
result = sol.search(t[0], t[1])
exp = t[2]
assert result == exp, (
f"\033[93m» {result} <> {exp}\033[91m for "
+ f"test {i} 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()