刷题篇–二分查找
思路很简单,细节是魔鬼。----二分查找
你真的了解二分查找吗?一般情况下二分查找要找到给的目标值,若没有找到则返回-1,这是传统技能。但是肯定会有幺蛾子出现,比如查找出数组中第一个出现或最后一个出现的位置、数组中有多少个要查找的元素等。
该文章将会梳理二分查找的一些基本内容,如果理解不够透彻,就直接记模板。
1.基本二分查找
nums = [3,4,5,7,8,8,8,10]
target = 5
low, high = 0, len(nums)-1 #赋值左右指针
while low <= high:
mid = low + (high - low) // 2 #这种写法是为了防止low+high过大溢出
if nums[mid] == target:
print(mid)
break
elif nums[mid] < target:
low = mid + 1
else:
high = mid - 1
这里要注意下找不到的情况,循环条件是low<=high,未找到的话终止条件肯定是low>high。实际上此时如果target不大于列表最大值,low也就是离target最近的大于它的那个位置;如果大于target,low最后应是len(nums)-1,这样讨论起来有些乱,所以索性我们直接再延申出两个关于上下界的模板。
2.寻找下界
前面已经说过,如果直接找某个target,上面的模板已经足够了。但是会出现一些变化的问题,如找到上下界等问题。这时讨论较复杂,所以我们直接写模板。
nums = [3,4,5,7,8,8,8,10]
target = 8
low, high = 0, len(nums)
while low < high:
mid = low + (high - low) // 2
if nums[mid] < target:
low = mid + 1
else:
high = mid
#low的结果为4,也就是它的下界(查找到的第一个target)
再来讨论一下low和high的问题,循环条件是low<high,最后退出循环时low=high,所以我们不用再纠结low和high的问题,因为最后他们是相同的。
如果low等于len(nums),或对应位置索引不等于target,则未找到。
if low == len(nums) or nums[low] != target: return False
再看一下找到target与未找到的区别
- 能够找到target,则low为找到的第一个值
- 如果未找到,则low为大于target的第一个值,如target = 6则返回low=3;target = 16(target>max(nums)) 则返回low=len(nums)
3.寻找上界
nums = [3,4,5,7,8,8,8,10]
target = 8
low, high = 0, len(nums)
while low < high:
mid = low + (high - low) // 2
if nums[mid] <= target:
low = mid + 1
else:
high = mid
#low的结果为7,也就是它的上界(查找到的最后一个target+1)
看一下找到target与未找到的区别
- 能够找到target,则low为找到的最后一个值加1
- 如果未找到,则low为大于target的第一个值,如target = 6则返回low=3;target = 16(target>=max(nums)) 则返回low=len(nums)
4.刷题题目
- leetcode34,在排序数组中查找元素的第一个和最后一个位置。
思路:该题可通过两次二分查找,找到上界和下界,得到第一个位置和最后一个位置。
def search_range(nums, target):
low, high = 0, len(nums)
while low < high:
mid = low + (high - low) // 2
if nums[mid] < target:
low = mid + 1
else:
high = mid
if low == len(nums) or nums[low] != target:
return [-1,-1] #判断是否在nums中,未找到就不需要执行下一步了
idx = low
low, high = 0, len(nums)
while low < high:
mid = low + (high - low) // 2
if nums[mid] <= target:
low = mid + 1
else:
high = mid
return [idx, low-1]
- leetcode69,x的平方根
该题解题方法较多,在这里使用二分法。相当于找到下界。
def my_sqrt(x):
l, r = 1, x+1 #此处位置即对应值
while l < r:
mid = (l+r) // 2
if mid * mid <= x:
l = mid + 1
else:
r = mid
return l-1
如果我们不使用上下界模板,使用基础的二分查找,则代码如下
def mySqrt(self, x):
l, r = 1, x
while l <= r:
mid = (l+r) // 2
if mid * mid == x:
return mid
elif mid * mid < x:
l = mid + 1
else:
r = mid - 1
return min(l, r)
此时需要讨论low和high的关系,在这里去出l和r的最小值。