二分查找是一种十分基础重要的搜索方法。一般多用于数组结构的搜索查找问题中。虽然思路简单,但是边界条件却很容易让人迷糊。这里对二分查找做一个总结。
首先以35题简单题做一个示范:
35 搜索插入位置](https://leetcode-cn.com/problems/search-insert-position/))
题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
做题思路
这道题的思路非常简单,就是利用二分查找,不断更新边界,最后锁定到某个插入位置。下面介绍两种具体写法中边界的不同考虑。
写法一
1 | class Solution { |
这种写法的关键在于左右边界所指的元素都在要考虑的区间内,也就是一种闭区间的写法,每次考虑的是[left,right]的情况,为了避免考虑过的区间重新考虑,我们就要在更新边界时将mid+1或者-1来赋值区间边界。
写法二
1 | class Solution { |
这种写法的关键在于右边界所指的元素并不在要考虑的区间内,是一种半开闭的区间状态,每次考虑的是[left,right)的情形。所以此时在更新状态时,更新右边界时直接让右边界等于mid就好,因为mid已经考虑过了。
当然在很多时候,像这样直白地告诉你我就是二分查找的题目并不是很多,再举一个二分查找解决的简单题,leetcode第69题,据说是一个常见的面试题目。
69 x-的平方根
题目描述
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入:4
输出:2
示例 2:
输入:8
输出:2
说明:8的平方根是2.82842…… 由于返回类型是整数,所以小数部分将会被舍去。
做题思路
本题其实是一个二分查找的典型应用场景,虽然看不到数组,但是显然自然数本身就是一个单调有序的排列,而x就是上界,0就是下界。这是因为因为题中提到小数部分会被舍去,同时,如果一个数的平方大于x,则它必然不可能是x的平方根。
这样这道题可以转换为,在数组[0,1,···,x]中,查找其中某个数的平方等于x,如果没有数的平方等于x,则找出平方小于x中最接近x的数。
这样看就是一个显然的二分查找了。
解法
1 | class Solution { |
以上两道题可以理解为二分查找找一个数的题目,接下来是一道二分查找找两个数的题目
34. 在排序数组中查找元素的第一个和最后一个位置
题目描述
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
- $ 0 <= nums.length <= 105$
- $-10^9 <= nums[i] <= 10^9$
- nums 是一个非递减数组
- $-10^9 <= target <= 10^9$
做题思路
思路上没有特别新鲜的地方,这道题的题解有趣之处在于函数的复用。具体思考见注释。
解法
1 | class Solution { |
原文链接: https://zijian.wang/2021/05/21/二分查找的区间选择问题/
版权声明: 转载请注明出处.