1
+ # 704. Binary Search
2
+ # 🟢 Easy
3
+ #
1
4
# https://leetcode.com/problems/binary-search/
5
+ #
6
+ # Tags: Array - Binary Search
2
7
3
8
4
9
import timeit
5
10
from typing import List
6
11
7
- # The loop solution is the most efficient on LeetCode.
8
- # Using the built-in method index is the most efficient locally.
12
+ # The iterative solution is the most efficient on LeetCode, using the
13
+ # built-in method index is the most efficient locally.
9
14
#
10
- # Index 0.01493 seconds
11
- # Loop 0.02365 seconds
15
+ # BuiltIn 0.01493 seconds
16
+ # Iterative 0.02365 seconds
12
17
# Recursive 0.04586 seconds
13
18
14
19
15
- # Use a loop to do binary search.
20
+ # Use a left and right pointer, at each iteration compute the mid-point
21
+ # between them, when the value at that mid index is greater than the
22
+ # target, we know that the target will be to the left if it found in the
23
+ # array, when the value is lesser, it will be found to the right.
24
+ #
25
+ # Time complexity: O(log(n)) - Each iteration discards half of the
26
+ # search space.
27
+ # Space complexity: O(1) - We use constant space.
16
28
#
17
- # Runtime: 249 ms, faster than 94.42% of Python3 online submissions for Binary Search.
18
- # Memory Usage: 15.4 MB, less than 71.46 % of Python3 online submissions for Binary Search.
19
- class Loop :
29
+ # Runtime 235 ms Beats 94.42%
30
+ # Memory 15.4 MB Beats 96.81%
31
+ class Iterative :
20
32
def search (self , nums : List [int ], target : int ) -> int :
21
- start = 0
22
- end = len (nums ) - 1
23
- while start <= end :
24
- if nums [start ] == target :
25
- return start
26
- if nums [end ] == target :
27
- return end
28
- mid = (start + end ) // 2
29
- if nums [mid ] == target :
30
- return mid
31
- if nums [mid ] > target :
32
- end = mid - 1
33
+ l , r = 0 , len (nums ) - 1
34
+ while l < r :
35
+ mid = (l + r ) // 2
33
36
if nums [mid ] < target :
34
- start = mid + 1
35
- return - 1
37
+ l = mid + 1
38
+ else :
39
+ r = mid
40
+ return l if nums [l ] == target else - 1
36
41
37
42
38
- # Use the built-in index method
43
+ # Use the built-in index method wrapped in a try-catch block, if the
44
+ # method does not find the target, it will throw an exception, catch it
45
+ # and return -1.
39
46
#
47
+ # Time complexity: O(log(n)) - Each iteration discards half of the
48
+ # search space.
49
+ # Space complexity: O(1) - We use constant space.
40
50
#
41
-
42
- # Use the built-in index method.
43
- #
44
- # Runtime: 343 ms, faster than 49.28% of Python3 online submissions for Binary Search.
45
- # Memory Usage: 15.5 MB, less than 71.46 % of Python3 online submissions for Binary Search.
46
-
47
-
48
- class Index :
51
+ # Runtime: 343 ms Beats 49.28%
52
+ # Memory 15.5 MB Beats 71.46%
53
+ class BuiltIn :
49
54
def search (self , nums : List [int ], target : int ) -> int :
50
55
try :
51
56
return nums .index (target )
@@ -55,52 +60,58 @@ def search(self, nums: List[int], target: int) -> int:
55
60
56
61
# Use a recursive call to implement binary search.
57
62
#
58
- # Runtime: 355 ms, faster than 45.00% of Python3 online submissions for Binary Search.
59
- # Memory Usage: 22.9 MB, less than 21.93 % of Python3 online submissions for Binary Search.
63
+ # Time complexity: O(log(n)) - Each iteration discards half of the
64
+ # search space.
65
+ # Space complexity: O(log(n)) - The height of the call stack will grow
66
+ # by one with each call to the recursive method.
67
+ #
68
+ # Runtime 355 ms Beats 45.00%
69
+ # Memory 22.9 MB Beats 21.93%
60
70
class Recursive :
61
71
def search (self , nums : List [int ], target : int ) -> int :
62
- def s (start : int , end : int ) -> int :
63
- if start > end :
72
+ def s (lo : int , hi : int ) -> int :
73
+ if lo > hi :
64
74
return - 1
65
- if nums [start ] == target :
66
- return start
67
- if nums [end ] == target :
68
- return end
69
- mid = (start + end ) // 2
75
+ mid = (lo + hi ) // 2
70
76
if nums [mid ] == target :
71
77
return mid
72
78
if nums [mid ] < target :
73
- return s (mid + 1 , end )
79
+ return s (mid + 1 , hi )
74
80
if nums [mid ] > target :
75
- return s (start , mid - 1 )
81
+ return s (lo , mid - 1 )
76
82
return - 1
77
- return s (0 , len (nums )- 1 )
83
+
84
+ return s (0 , len (nums ) - 1 )
78
85
79
86
80
87
def test ():
81
- executor = [
82
- { 'executor' : Index , 'title' : 'Index' , } ,
83
- { 'executor' : Loop , 'title' : 'Loop' , } ,
84
- { 'executor' : Recursive , 'title' : 'Recursive' , } ,
88
+ executors = [
89
+ Iterative ,
90
+ BuiltIn ,
91
+ Recursive ,
85
92
]
86
93
tests = [
87
- [[], 2 , - 1 ],
88
94
[[5 ], 5 , 0 ],
89
95
[[- 4 , - 2 , 0 , 1 ], 2 , - 1 ],
90
96
[[- 1 , 0 , 3 , 5 , 9 , 12 ], 9 , 4 ],
91
97
[[- 1 , 0 , 3 , 5 , 9 , 12 ], 2 , - 1 ],
92
98
]
93
- for e in executor :
99
+ for executor in executors :
94
100
start = timeit .default_timer ()
95
- for _ in range (int (float ('1e4' ))):
96
- for t in tests :
97
- sol = e ['executor' ]()
98
- result = sol .search ([* t [0 ]], t [1 ])
99
- expected = t [2 ]
100
- assert result == expected , f'{ result } != { expected } for { t [0 ]} :{ t [1 ]} using { e ["title" ]} solution'
101
+ for _ in range (1 ):
102
+ for col , t in enumerate (tests ):
103
+ sol = executor ()
104
+ result = sol .search (t [0 ], t [1 ])
105
+ exp = t [2 ]
106
+ assert result == exp , (
107
+ f"\033 [93m» { result } <> { exp } \033 [91m for"
108
+ + f" test { col } using \033 [1m{ executor .__name__ } "
109
+ )
101
110
stop = timeit .default_timer ()
102
111
used = str (round (stop - start , 5 ))
103
- print ("{0:20}{1:10}{2:10}" .format (e ['title' ], used , "seconds" ))
112
+ cols = "{0:20}{1:10}{2:10}"
113
+ res = cols .format (executor .__name__ , used , "seconds" )
114
+ print (f"\033 [92m» { res } \033 [0m" )
104
115
105
116
106
117
test ()
0 commit comments