代码随想录算法训练营第一天| 704. 二分查找
ctrl老师文章链接
:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
视频讲解:https://www.bilibili.com/video/BV1fA4y1o715
704. Binary Search
说到二分搜索一提起来大家都觉得很简单,就是就是不断地随小范围,最终随缩小到只剩一个或者恰好端点就是要找的提前结束,大部分人心里都有个框架,但是二分搜索却又没有这么简单,这里由两个问题一个是while(left right)到底是小于还是小于等于,另一个是right = middle呢,还是right = middle -1。我在学习二分搜索的时候完全是在乱写,完全不知道该依据什么来写,如果ac救过,报错看眼答案是小于等于就改成小于等于,并没有意识到这两个问题是配套的,稀里糊涂就过去了,看了卡尔老师讲解才明白,之所以不明白条件该如何写是因为区间没有想明白,每次循环要保证区间定义是不变的,区间定义决定了条件设置,比如left小于等于right说明right在区间内,那么我们采用的区间其实就是左闭右闭,如果left小于right,说明right不在区间内,那么说明是左闭右开
我们这里使用两种区间定义的方法,左闭右闭即[left, right],或者左闭右开即[left, right)。首先确定你要选择的区间定义,然后根据区间决定两个边界条件如何设置
- 左闭右闭
此时循环条件应为left<=right,因为当left=right,应该被包含在[left, right]区间内,因此while条件应带有等于这种情况
当middle= left+(right-left)//2
那么当我们发现target在mid的左面,那么right需要更新,此时因为target和mid处的值不相等,说明下一个搜索的范围不应该含有middle这个位置,区间范围是左闭右闭,那么区间的两个端点都应该在搜索范围,也就是说区间的所有点都有可能等于target。因为已经明确知道了middle这个点与target不相等,应该从middle-1开始搜索,因此right=middle-1。当target在middle的右面,说明left需要更新,同理明确知道middle不等于target,不应该在左闭右闭的范围中,那么left=middle+1
注意如果middle=(left+right)/2有时候相加会越界,为了避免这种情况,我们使用left+(right-left)//2确保不会出问题
那么大家可能会有疑惑,middle也就比middle-1多了一个点,也就浪费一点时间,无所谓。那就问题大了,区间的定义发生了改变,如果不对middle做任何处理,当想找的点不在数组中会造成死循环。, - 左闭右开
此时循环条件为left<right,因为当left=right不被包含在[left, right)区间中,
那么当target在middle的左面,那么right需要更新,,并且需要去左区间继续寻找,而寻找区间是左闭右开区间, right不在区间内,我们目前知道middle不等于target,因此middle不在区间内,但是middle-1是否在区间不清楚,那么假如让right=middle-1,如果middle-1在区间内,而right却不在区间,则把middle-1忽略了,因此就让right=middle就可以了,那么当target在middle的右面,需要更新left,但是左闭右开区间,left在区间内,因此left要等于middle+1,如左闭右闭道理一样。还有个十分容易被忽略的就是right的初始化,因为right是开区间,所以right如果取len(给定数组)-1,那么此时right是在区间内的,而len(给定数组)不在区间内,所以应该取后者
1. class Solution:
2. def search(self, nums: List[int], target: int) -> int:
3. #[left,right)
4. left,right=0,len(nums)
5. while left<right:
6. mid=left+(right-left)//2
7. if nums[mid]<target:
8. left=mid+1
9. elif nums[mid]>target:
10. right=mid
11. else:
12. return mid
13. return -1
14.
15. #[left,right]
16. left,right=0,len(nums)-1
17. while left<=right:
18. mid=left+(right-left)//2
19. if nums[mid]<target:
20. left=mid+1
21. elif nums[mid]>target:
22. right=mid-1
23. else:
24. return mid
25. return -1
26. # return -1