给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
数组本来就是有序的,适合使用二分查找算法,二分查找比较容易理解,查找范围为【left,right】,初始查找整个数组,每次取出左右端点的中点值与目标值进行比较,如果相等直接返回中点值下标,如果不等的话,根据大小,移动左端点或者右端点,缩小一半的查找范围,直到左端点超过右端点,循环结束。
class Solution {
public:
int search(vector<int>& nums, int target) {
//二分查找:本身就是有序的,进行查找
//左右端点
int left=0,right=nums.size()-1;
//当左端点大于右端点时,循环结束
//注意重合时,中点值也可能是目标值
while(left<=right)
{
//中点值下标
int mid=(left+right)/2;
//如果中点值就是要查找的,直接返回
if(nums[mid]==target) return mid;
//如果比较小,左端点向右挪动
else if(nums[mid]<target)
{
left=mid+1;
}
//如果较大,右端点向左挪动
else
{
right=mid-1;
}
}
//如果循环结束还没返回,说明没找到,返回-1
return -1;
}
};
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
输入:n = 1, bad = 1
输出:1
从第一个出错的版本为分界线,之前的所有版本为正确的,之后的所有版本都出错,那么相当于有序,我们可以利用二分查找,不断缩小范围。这里默认一定有第一个错误版本,所以循环判断中点的版本是否正确,如果是正确的,说明第一个出错版本在区间【mid+1,right】中,如果错误,那么在前面的区间【left,mid】中。循环缩小范围,直到最后左右端点重合,循环结束,返回这个重合点,就是要找的第一个出错版本。
注:为防止计算时溢出,中点值用mid=left+(right-left)/2
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
int left=1,right=n;
//判断中点是正确与否
while(left<right)
{
int mid=left+(right-left)/2;//防止计算溢出
bool m=isBadVersion(mid);
//如果中点为错误版本,那么第一个错误版本在【left,mid】中
if(m)
{
right=mid;
}
//如果中点为正确版本,那么第一个错误版本在【mid+1,right】中
else
{
left=mid+1;
}
}
//当left==right时,已经缩小范围到一个点,肯定就是他了
return left;
}
};
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
输入: nums = [1,3,5,6], target = 5
输出: 2
输入: nums = [1,3,5,6], target = 7
输出: 4
这里使用的也是二分查找法,值得注意的是如果查找失败,返回的是要插入的位置,这里当循环到左右端点重合时,即最后一次循环,有三种情况:
1.中间值就是目标值,还是直接返回
2.中间值不是目标值,那么要返回最后目标值要插入的位置:
(1)中间值比目标值小,插入位置就是中间值的下一位(mid+1)
(2)中间值比目标值大,插入位置就是中间值所在位置(mid)
可以看到,返回的插入值刚好是更新后的left的值,所以查找失败,直接返回left即可。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
//插入排序
int left=0,right=nums.size()-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]<target)
{
left=mid+1;
}
else
{
right=mid-1;
}
}
//查找不成功,直接返回left。
return left;
}
};