二分查找习题篇(上)
1.二分查找
题目描述:
解法一:暴力解法
解法二:二分查找算法
当数组具有“二段性”时,我们就可以用二分查找算法。
算法思路:
细节问题:
1.循环结束的条件
2.为什么是正确的?
3.时间复杂度
代码实现:
class Solution {
public:
int search(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) right=mid-1;
else if(nums[mid]<target) left=mid+1;
else return mid;
}
return -1;
}
};
朴素二分模版:
while(left<=right)//每次查找的元素都是未知的,所以要取等号
{
int mid=left+(right-left)/2; //防止溢出,替换成left+(right-left+1)也可以,只不过是偶数个元素时,mid有2个中间值,左右均可
if(……)
right=mid-1;
else if(……)
left=mid+1;
else
return ……;
}
2.在排序数组中查找元素的第一个和最后一个位置
题目描述:
解法一:暴力查找
解法二:二分查找
当数组具有“二段性”时,我们就可以用二分查找算法。接下来我们来寻找该数组的“二段性”.
记左边界为Bleft,右边界为Bright.
1.查找区间的左端点
细节处理:
1.循环条件:
2.求中点的操作:
2.查找区间右端点:
细节处理:
1.循环条件:
2.求中点的操作:
代码实现:
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
// 处理边界情况
if(nums.size() == 0)
return {-1, -1};
int begin = 0;
// 1. 二分左端点
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target)
left = mid + 1;
else
right = mid;
}
// 判断是否有结果
if(nums[left] != target)
return {-1, -1};
else
begin = left; // 标记⼀下左端点
// 2. 二分右端点
left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(nums[mid] <= target)
left = mid;
else
right = mid - 1;
}
return {begin, right};
}
};
查找区间左端点的模版:
while(left < right)
{
int mid = left + (right - left) / 2;
if(……) left = mid + 1;
else right = mid;
}
查找区间右端点的模版:
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(……) left = mid;
else right = mid - 1;
}
3.搜索插入位置
题目描述:
算法思路:
本题数组是一个排序数组,具有“二段性”时,我们就可以用二分查找算法。
代码实现:
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)
left = mid + 1;
else
right = mid;
}
if(nums[left] < target)
return right + 1;
return right;
}
};
4.x 的平方根
题目描述:
解法一:暴力查找
算法思路:
依次枚举 [0, x] 之间的所有数 i :
-
如果 i * i == x ,直接返回 x ;
-
如果 i * i > x ,说明之前的⼀个数是结果,返回 i - 1 。
代码实现:
class Solution {
public:
int mySqrt(int x) {
// 由于两个较⼤的数相乘可能会超过 int 最⼤范围
// 因此⽤ long long
long long i = 0;
for (i = 0; i <= x; i++)
{
// 如果两个数相乘正好等于 x,直接返回 i
if (i * i == x) return i;
// 如果第⼀次出现两个数相乘⼤于 x,说明结果是前⼀个数
if (i * i > x) return i - 1;
}
// 为了处理oj题需要控制所有路径都有返回值
return -1;
}
};
解法二:二分查找
本题数组具有“二段性”时,我们就可以用二分查找算法。
算法思路:
代码实现:
class Solution
{
public:
int mySqrt(int x)
{
if(x < 1) return 0; // 处理边界情况
int left = 1, right = x;
while(left < right)
{
long long mid = left + (right - left + 1) / 2; // 防溢出
if(mid * mid <= x) left = mid;
else right = mid - 1;
}
return left;
}
};
最后,本篇文章到此结束,感觉不错的友友们可以一键三连支持一下笔者,有任何问题欢迎在评论区留言哦~