-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfind-first-and-last-position-of-element-in-sorted-array.py
More file actions
141 lines (110 loc) · 4.87 KB
/
find-first-and-last-position-of-element-in-sorted-array.py
File metadata and controls
141 lines (110 loc) · 4.87 KB
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
# https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/
# Related Topics: Array, Binary Search
# Difficulty: Medium
# Initial thoughts:
# A naive approach is to look at every element of nums to find the beginning
# and end of target. This approach's Time complexity is O(n).
# To reduce the Time complexity to O(log n) we are going to use binary search.
# Let's say we find target at nums[i]. If nums[i-1] === nums[i] we are going to
# binary search againt for target between 0 and i and repeat this process until
# we find an index k where either k === 0 or nums[k-1] < nums[k].
# The same goes for the right bound of our range. If nums[i+1] === nums[i]
# we are going to binary search for target between i and nums.length-1 until we
# find an index k where either k === nums.length-1 or nums[k] < nums[k+1].
# This will be a multitude of log ns at most.
# Time complexity: O(log n) where n === nums.length
# Space complexity: O(log n) because of the recursive binary search
from typing import List
import bisect
# Note: Everything after searchRange2 is old and not optimal
# I realize that I've learned some lessons over the past couple of years
class Solution:
# Time complexity: O(log n) where n is the length of nums
# Space complexity: O(1)
# Implementing my own bisect_left/bisect_right functions
def searchRange(self, nums: List[int], target: int) -> List[int]:
def bisect_left(arr, target):
left, right = 0, len(arr)
while left < right:
mid = left + (right - left) // 2
if target > arr[mid]:
left = mid+1
else:
right = mid
return left
def bisect_right(arr, target):
left, right = 0, len(arr)
while left < right:
mid = left + (right - left) // 2
if target < arr[mid]:
right = mid
else:
left = mid+1
return left
if len(nums) == 0: return [-1, -1]
left = bisect_left(nums, target)
if left >= len(nums) or nums[left] != target:
return [-1, -1]
right = bisect_right(nums, target)
return [left, right-1]
# Time complexity: O(log n) where n is the length of nums
# Space compleixty: O(1)
# Using pythons built-in bisect module
def searchRange2(self, nums: List[int], target: int) -> List[int]:
if len(nums) == 0: return [-1, -1]
left = bisect.bisect_left(nums, target)
if left >= len(nums) or nums[left] != target:
return [-1, -1]
right = bisect.bisect_right(nums, target)
return [left, right-1]
def searchRange3(self, nums: List[int], target: int) -> List[int]:
def BinarySearch(nums: List[int], target: int, low: int, high: int) -> int:
if low > high:
return -1
mid = low + (high-low)//2
if nums[mid] == target:
return mid
elif nums[mid] > target:
return BinarySearch(nums, target, low, mid-1)
else:
return BinarySearch(nums, target, mid+1, high)
index = BinarySearch(nums, target, 0, len(nums)-1)
if index == -1:
return [-1, -1, ]
# Find left bound
left = index
while left > 0 and nums[left] == nums[left-1]:
left = BinarySearch(nums, target, 0, left-1)
# Find right bound
right = index
while right < len(nums)-1 and nums[right] == nums[right+1]:
right = BinarySearch(nums, target, right+1, len(nums))
return [left, right]
# Optimization:
# Using a iterative binary search we can render the space complexity constant
# although this will arguably have a less readable code
# Time Complexity: O(log n)
# Space Complexity: O(1)
def searchRange4(self, nums: List[int], target: int) -> List[int]:
def BinarySearchIterative(nums: List[int], target: int, low: int, high: int) -> int:
while low <= high:
mid = low + (high-low)//2
if nums[mid] == target:
return mid
elif nums[mid] > target:
high = mid-1
else:
low = mid+1
return -1
index = BinarySearchIterative(nums, target, 0, len(nums))
if index == -1:
return [-1, -1]
# Find left bound
left = index
while left > 0 and nums[left] == nums[left-1]:
left = BinarySearchIterative(nums, target, 0, left-1)
# Find right bound
right = index
while right < len(nums)-1 and nums[right] == nums[right+1]:
right = BinarySearchIterative(nums, target, right+1, len(nums)-1)
return [left, right]