一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
二分查找背了模板之后发现很多时候并不能灵活的使用到题中。因此本文针对几种情况讨论如果确定条件
题目描述
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
这是最简单的二分了
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size();
while(left < right){
int mid = (left + right ) / 2;
if(nums[mid] > target){
right = mid;
}else if (nums[mid] < target){
left = mid;
}else {
return mid;
}
}
return nums[left] == target?left:-1;
}
这么一写,就发现超时了,那怎么会超时呢?
思路分析
这类问题是因为mid很可能在left = 2n,right=2n+1时仍然等于2n,这样的话很可能造成死循环,回归题意,我们使用二分其实是想确定一个范围为1的区间,当这个区间里元素等于target时,则认为找到了,而对于(nums[mid] > target),其实是不需要再判断mid的,因此可以调整为right-1。
这只是一个开始
题目再描述
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
问题关键在于,此刻我们不能在nums[mid] = target的时候返回,而是要将其设为right = mid,然而在这个时候又出现了死循环的隐患。
我们不妨换个思路,还是只找一个值但是找的是target开始位置前一个值,以及开始位置后一个值
while (left < right) {
int mid = (right - left) / 2 + left;
if (nums[mid] >= target) {
right = mid - 1;
}else {
left = mid + 1;
}
}
这个时候代码会类似这个样子,此时想找到这个值只需要再修正一次即可,即对最后的结果根据需要进行加一或减一,当然也可以直接修改为while (left <= right) {