给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
分析:由于数组是一个有序数组,故可以采用二分搜索找位置,可以在 O ( l o g n ) O(logn) O(logn) 的时间复杂度内找到。
//暴力的方法:
class Solution {
public:
int searchInsert(vector<int>& nums, int target)
{
for(int i=0;i<nums.size();i++)
{
if(nums[i]>=target)
return i;
}
return nums.size();
}
};
//二分查找的方法:
class Solution {
public:
int searchInsert(vector<int>& nums, int target)
{
int index=-1;
int begin=0,end=nums.size()-1;
while(index==-1) //未找到位置
{
int mid=(begin+end)/2;
if(target==nums[mid])
index=mid;
else if(target<nums[mid]) //mid处值大于target
{
if(mid==0||target>nums[mid-1]) //短路,不用判断mid-1非法
index=mid; //确定了target的位置
end=mid-1; //因为已经判断过mid的位置,直接mid-1即可
}
else if(target>nums[mid])
{
if(mid==nums.size()-1||target<nums[mid+1])
index=mid+1;
begin=mid+1;
}
}
return index;
}
};
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去。
分析:x的平方根,在此处是指最大的满足 r*r<=x的r值,故可以用二分查找,因为该值一定在0~x间,故可以逐步判断每次减少一半区间,在 O ( l o g n ) O(logn) O(logn) 的时间复杂度内找到其应该在的位置
class Solution {
public:
int mySqrt(int x)
{
int left = 0, right = x, ans = -1;
while (left <= right)
{
int mid = (left+right)/ 2;
if ((long long)mid * mid <= x) //避免溢出
{
ans = mid;
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return ans;
}
};
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
分析:实际上,要找的位置,可以这样理解:如果target没有出现,则返回[-1,-1],否则,返回target的最左索引和target的最右索引。由于数组是排序的,因此采用二分查找,思路如下:
class Solution {
public:
int left_bound(vector<int> & nums,int target)
{
int begin=0,end=nums.size()-1;
while(begin<=end)
{
int mid=(begin+end)/2;
if(target==nums[mid]) //相等时候还要找到最左侧的该值
{
if(mid==0||nums[mid-1]<target) //说明是最左的索引了
return mid;
end=mid-1; //否则令end左移
}
else if(target<nums[mid])
end=mid-1;
else if(target>nums[mid])
begin=mid+1;
}
return -1;
}
int right_bound(vector<int> & nums,int target)
{
int begin=0,end=nums.size()-1;
while(begin<=end)
{
int mid=(begin+end)/2;
if(target==nums[mid]) //相等时候还要找到最右侧的该值
{
if(mid==nums.size()-1||nums[mid+1]>target) //说明是最右的索引了
return mid;
begin=mid+1; //否则令end左移
}
else if(target<nums[mid])
end=mid-1;
else if(target>nums[mid])
begin=mid+1;
}
return -1;
}
vector<int> searchRange(vector<int>& nums, int target)
{
vector<int> result;
int left=left_bound(nums,target);
int right=0;
if(left==-1)
right=-1;
else
right=right_bound(nums,target);
result.push_back(left);
result.push_back(right);
return result;
}
};
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
注意数组中可能存在重复的元素。
示例 1:
输入: [1,3,5]
输出: 1
示例 2:
输入: [2,2,2,0,1]
输出: 0
分析:原始数组为一个非递减排序,因此,我们旋转后,可知该数组应当是两个非递减序列组成,并且右侧序列的最右端(即最大值)一定不大于左侧序列的最左端(即最小值)
可以举一个具体例子来分析
给定数组 1,2,3,4,5
旋转后可以为 3,4,5,1,2
因此可以看出 3,4,5 1,2 分别是非递减序列
因此根据该性质,易知可用二分查找
class Solution {
public:
int findMin(vector<int>& nums)
{
int size=nums.size();
int i=0,j=size-1;
while(i<j)
{
if(nums[i]<nums[j]) //说明数组没有旋转
return nums[i];
int mid=(i+j)>>1;
if(nums[mid]>nums[j])
{
i=mid+1;continue; //注意这里i=mid+1
}
if(nums[mid]<nums[j])
{
j=mid; //j=mid 主要是让i和j必须保持相近,避免二者相邻时出现死循环
continue;
}
if(nums[mid]==nums[j])//两个值相等的话没办法二分,只能逐步右移
{
i++;
}
}
return nums[i];
}
};
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
分析:为了达到 O ( l o g n ) O(logn) O(logn) 的时间复杂度,因此需要采用二分查找。该题采用二分查找时需要注意的是,每次找到中点后,不应该直接去比较中点与target的值,而应该首先确定左右两个区间是否是完全递增的。完全递增的区间和两个分别递增的区间的判断方式不一样。
class Solution {
public:
int search(vector<int>& nums, int target)
{
int begin=0,end=nums.size()-1;
while(begin<=end)
{
int mid=(begin+end)/2;
if(target==nums[mid])
return mid;
else if(target<nums[mid])
{
if(nums[begin]<nums[mid])//左侧递增
{
if(target>=nums[begin])
end=mid-1;
else
begin=mid+1;
}
else if(nums[begin]>nums[mid])//左侧旋转
{
end=mid-1;
}
else if(nums[begin]==nums[mid]) //只剩下两个元素
{
begin=mid+1;
}
}
else if(target>nums[mid])
{
if(nums[begin]<nums[mid])//左侧递增
{
begin=mid+1;
}
else if(nums[begin]>nums[mid]) //左侧旋转
{
if(target>=nums[begin])
end=mid-1;
else
begin=mid+1;
}
else if(nums[begin]==nums[mid]) //只剩两个元素
begin=mid+1;
}
}
return -1;
}
};