【leetcode-Python】-二分搜尋-34 Find First and Last Position of Element in Sorted Array
技術標籤:leetcode
目錄
題目連結
34. Find First and Last Position of Element in Sorted Array
題目描述
給定非降序整數陣列nums,找出元素值等於target的索引區間。如果陣列中不存在target,返回[-1,-1]。
約束條件:
0 <= nums.length <=
-<= nums[i]<=
nums
is a non-decreasing array.-<= target<=
示例
輸入:nums = [5,7,7,8,8,10],target = 8
輸出:[3,4]
輸入:nums = [5,7,7,8,8,10],target = 6
輸出:[-1,-1]
輸入:nums = [],target = 0
輸出:[-1,-1]
解決思路一
1、先處理特例,當nums為空時,任何target都無法在nums中被找到,因此直接返回[-1,-1]。
2、利用二分搜尋演算法找出目標索引區間的左端點,即陣列中第一個等於target的元素索引值start,如果找不到設定start為-1。
3、找出目標索引區間的右端點,即陣列中最後一個等於target的元素索引值end,如果找不到設定end為-1。具體策略為找出陣列中第一個大於target的元素nums[left],如果nums[left]是陣列的第一個元素,設定end = -1,如果nums[left-1]存在,檢驗nums[left-1]是否等於target,如果不等於說明target在陣列中不存在,則設定end = -1。
解決思路一Python實現
class Solution: def searchRange(self, nums: List[int], target: int) -> List[int]: #首先找出starting position if(len(nums) == 0): return [-1,-1] left = 0 right = len(nums) while(left < right): mid = (left + right) // 2 if(nums[mid] >= target): right = mid else: left = mid + 1 if(left != len(nums) and nums[left] == target): start = left else: start = -1 #find the ending position left = 0 right = len(nums) while(left < right): mid = (left + right) // 2 if(nums[mid] > target): right = mid else: left = mid + 1 end = left - 1 #left為num中第一個大於target的索引,該索引減去1的結果可能是元素值等於target的最大索引。 if(left != 0 and nums[end] != target): #對left進行檢驗,如果end索引能夠訪問並且nums[end]不等於target,說明陣列中不存在target end = -1 return [start,end]
在尋找目標序列的右端點時,如果設定right初始化為len(nums)-1,當left == right 條件滿足跳出迴圈時,需要判斷nums[left]是否等於target(因為有可能right沒有移動,left一直移動到和right重合等於len(nums)-1,nums[left]有可能等於target),如果等於,那麼end應該設為left。程式碼如下,但是因為後續要考慮的東西更多,不推薦這種初始化方式(如果搜尋條件不涉及到對nums[right]的訪問,right變數初始化儘量都設定為len(nums),按照標準配置來寫不容易出現邊界錯誤)。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
#首先找出starting position
if(len(nums) == 0):
return [-1,-1]
left = 0
right = len(nums)
while(left < right):
mid = (left + right) // 2
if(nums[mid] >= target):
right = mid
else:
left = mid + 1
if(left != len(nums) and nums[left] == target):
start = left
else:
start = -1
#find the ending position
left = 0
##############right初始化值被修改###################
right = len(nums)-1
while(left < right):
mid = (left + right) // 2
if(nums[mid] > target):
right = mid
else:
left = mid + 1
#當left == right時跳出迴圈,如果right被初始化為len(nums)-1,那麼跳出迴圈後需要對nums[left]進行檢驗
if(nums[left] == target):
left += 1
end = left - 1 #left為num中第一個大於target的索引,該索引減去1的結果可能是元素值等於target的最大索引。
if(left != 0 and nums[end] != target): #對end進行檢驗,如果end索引能夠訪問並且nums[end]不等於target,說明陣列中不存在target
end = -1
return [start,end]
解決思路二
1、尋找目標區間的左邊界,並根據左邊界的尋找情況判斷陣列中是否存在target(如果沒有找到左邊界,說明陣列中不存在值等於target的元素)。
2、在左邊界存在的前提下,尋找右邊界(找出陣列中第一個大於target的元素,該元素的索引減去1即為右邊界)。
設定一個findRange函式,傳入nums陣列和is_left變數。如果is_left為True,說明findRange函式找的是
左邊界,當nums[mid]==target時,由right變數暫存mid同時在[left,mid)區間繼續搜尋(因為尋找的是左邊界,左邊界要麼是mid要麼在mid的左邊);如果is_left為False,說明findRange函式尋找的是第一個比target大的元素(右邊界+1),因此當nums[mid]==target時,更新left = mid + 1,在mid右邊進行搜尋。
解決思路二Python實現
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def findRange(nums,is_left):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] > target or (nums[mid] == target and is_left)):
right = mid
else:
left = mid + 1
return left
left_index = findRange(nums,is_left = True)
if left_index == len(nums) or nums[left_index] != target:
return [-1,-1]
right_index = findRange(nums,is_left = False)
return [left_index,right_index-1]
抽取出尋找右邊界的程式碼:
def findRange(nums):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] > target):
right = mid
else:
left = mid + 1
if(left == 0):
return -1
return nums[left-1] if nums[left-1] == target else -1
解決思路三
直接利用二分搜尋來尋找右邊界,不需要先找第一個大於target的元素再令其索引值減1。
“用二分搜尋法尋找右邊界”和“用二分搜尋法尋找左邊界”屬於映象問題。用二分搜尋法尋找左邊界的思路為:迴圈跳出條件為left == right,計算mid = (left + right) //2,當nums[mid] >= target時,更新right = mid,由right暫存可能的左邊界;否則更新left = mid + 1。最後對nums[left]進行判斷。程式碼如下:
def findLeftRange(nums):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] >= target):
right = mid
else:
left = mid + 1
if(left != len(nums) and nums[left] == target):
return left
return -1
用二分搜尋演算法尋找右邊界(類似找nums[i]<=target的右邊界)的思路為:迴圈跳出條件為left == right,計算mid = (left + right) // 2 + 1,當nums[mid] > target時,right = mid - 1,當nums[mid] <= target時,left = mid,由left暫存可能的右邊界。mid的計算方式設定為(left + right) // 2 + 1的原因為,當left + 1 == right時,按照mid = (left + right) // 2來計算,mid和left重合,如果滿足nums[mid] <= target,仍然賦值left = mid,這樣left、right都沒有移動,會陷入死迴圈。如果按照 mid =(left + right) // 2 + 1來計算,當left + 1 == right時,mid和right重合,無論nums[mid]和target的大小關係是怎樣的,迴圈都能終止(但需要將right初始化為len(nums)-1,否則當mid和right重合且right = len(nums)時對nums[mid]的訪問會報錯)。
程式碼如下:
def findRightRange(nums):
left = 0
right = len(nums)-1
while(left<right):
mid = ((left + right) // 2) + 1
if(nums[mid] <= target):
left = mid
else:
right = mid - 1
if(right != -1 and nums[right] == target):
return right
return -1
解決思路三Python實現
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def findLeftRange(nums):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] >= target):
right = mid
else:
left = mid + 1
if(left != len(nums) and nums[left] == target):
return left
return -1
def findRightRange(nums):
left = 0
right = len(nums)-1
while(left<right):
mid = ((left + right) // 2) + 1
if(nums[mid] <= target):
left = mid
else:
right = mid - 1
if(right != -1 and nums[right] == target):
return right
return -1
return [findLeftRange(nums),findRightRange(nums)]