题目:Given an array of integers nums
sorted in ascending order, find the starting and ending position of a given target
value. Your algorithm's runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1]
.
代码如下:
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if len(nums)==0:
return [-1,-1]
start,end=-1,-1
low,high=0,len(nums)-1
while low<=high:
mid=(low+high)//2
if nums[mid]<target:
low=mid+1
elif nums[mid]>target:
high=mid-1
else:
start,end=mid,mid
break
else:
return [start,end]
#先求start
tmp_high=start
while low<tmp_high:
mid=(low+tmp_high)//2
if nums[mid]==target:
tmp_high=mid
else:
low=mid+1
start=low
#求end
tmp_low=end
while tmp_low<high:
mid=(tmp_low+high+1)//2
if nums[mid]==target:
tmp_low=mid
else:
high=mid-1
end=tmp_low
return [start,end]
具体思路如下:
首先,题目对时间复杂度有要求,这题还是查找,而查找算法中满足logn复杂度的有折半查找。所以代码中第一段while结构基本与这般查找相同.具体判断了三种情况:
当nums[mid]<target时,所有的target只能在mid的右侧,所以low=mid+1
当nums[mid]>target时,所有的target只能在mid的左侧,所有high=mid-1
而当nums[mid]==target时,target的开始和结束位置可能在mid的两侧。而要找到target的开始和结束的位置,在midd的两侧都有进行查找,所以这个时候只能跳出当前的while循环,对mid两侧的两段有序序列分别进行查找。(mid左右两侧都是有序序列,同样可以利用折半查找。)
else子句处理target不在nums序列中的情况,直接return [-1,-1]
下面进入两个while循环。
第一个while循环求start。在这个循环中所做的工作就是将low不断地想tmp_high靠近。
当nums[mid]=target时,将tmp_high放置到mid的位置。这里要说明一下,因为在寻找start的时候,我们实际要找的是索引值最小的start,所以我们要保证low和tmp_high的两个索引对应的值必须有一个一直为target,否则在循环结束的时候没办法确定target的开始索引。
第二个while循环去end。在这个循环中所有的工作就是不断移动high。大体的思想与前一个whild循环类似。我重点要讲的是下面这个问题。下面贴出的代码是我第一次的代码版本,这两段代码的区别在于mid值的计算,在求start的循环中,mid的计算是向下取整,而在这里则是向上取整。
while tmp_low<high:
mid=(tmp_low+high)//2
if nums[mid]==target:
tmp_low=mid
else:
high=mid-1
end=tmp_low
我在执行这个版本的代码时,提示超时。也就意味着我这段代码导致了死循环。这个死循环的原因主要是这个代码无法处理一下情况:
在这种情况下,tmp_low永远没有办法等于high,mid用于等于tmp_low,所以不会结束循环,tmp_low也没有定位到target的结束索引,所以在最终提交的代码版本里改成了向上取整。循环既可以正常结束,也能正常定位到结束索引。
在求start的while循环里,出现这种情况时,high会向low移动,所以不会死循环,也就能正常定位开始索引。以上就是对于这一题的总结。如有误,请纠正。