前言
我一直不知道二分查找有这么多练习和变式,本以为是一个简单深邃的思想用来学习,。但是啊,他的具体应用就没有那么简单了。因此,我配合着leetcode和洛谷的题目和学习资料,努力的刷通关知识点,并且努力的把知识点归纳总结,与君共勉
知识点简介
目录
思路
leetcode入门题目
【ps一下,leetcode的提交和acm训练题目是不一样的,大家可以自行学习一下,免得看不懂,不过确实不难哦】
题目链接力扣
二分查找
思路【例子】
代码
class Solution {
public:
int search(vector<int>& nums, int target) {
int l,r,mid;
l=0;
r=nums.size()-1; //数组长度减一
while(l<=r)
{
mid=(l+r)/2;
if(nums[mid]==target)
return mid; //找到才返回
else if(nums[mid]<target)
l=mid+1;
else
r=mid-1;
}
return -1;
}
};
题目链接 力扣
第一个错误版本
思路
和前面不一样的是,虽然有序,但是我们要找到第一个出问题的版本,所以把正确的左区间不断压缩。当 l与r相等输出l
代码
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
int l=1,r=n,mid;
while(l<r)
{
mid=(r-l)/2+l;
if(isBadVersion(mid))
r=mid;
else
l=mid+1;
}
return r;
}
};
题目链接力扣
x的平方根
代码
class Solution {
public :
int mySqrt(int x) {
int l,r,mid;
l=1;r=x;
if(x==0)
return 0;
while(l<=r)
{
mid=(r-l)/2+l;
if(mid<=x/mid && (mid+1)>x/(mid+1))
return mid;
else if(mid>x/mid)
r=mid-1;
else if((mid+1)<=x/(mid+1))
l=mid+1;
}
return -1;
}
};
题目链接力扣
搜索插入位置
代码
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l,r,mid;
l=0;r=nums.size()-1;
while(l<=r)
{
mid=(r-l)/2+l;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
r=mid-1;
else
l=mid+1;
}
return l;
}
};
题目链接力扣
搜索旋转排序数组
思路
代码
class Solution {
public:
int search(vector<int>& nums, int target) {
int l,r,mid;
l=0;
r=nums.size()-1; //数组末尾
while(l<=r)
{
mid=(r-l)/2+l; //中间位置
if(nums[mid]==target)
return mid; //返回
else if(nums[l]<=nums[mid]) //mid在左区间
{
if(nums[mid]>target && target>=nums[l]) //目标数在左区间
r=mid-1;
else
l=mid+1; //目标函数在右区间
}
else //mid在右区间
{
if(target>nums[mid] &&target<=nums[r])
l=mid+1;
else
r=mid-1;
}
}
return -1; //找不到
}
};
题目链接力扣
寻找峰值
思路
这个题我自己撞进了一个误区,因为数组第一个和最后一个都很小,题目里说的很清楚,【你可以假设 nums[-1] = nums[n] = -∞】我当时一直假设他可能在后半段一直单调递增。峰值元素是指其值大于左右相邻值的元素——这个表示是至少递增序列间断插入一个小一点的数,所以在后半段一定有峰值。而且只要输出一个返回值就好
所以我想到一句话,当你找不到思路和方法时,为什么不再看一遍题目呢。所以不管怎么说
代码
class Solution {
public:
int findPeakElement(vector<int>& nums) {
if(nums.size()<=1){
return 0;
}
for (int i = 0; i < nums.size()-1; i++) {
if(nums[i]>nums[i+1]){
return i;
}
}
return nums.size()-1;
}
};
题目链接力扣
寻找旋转排序数组中的最小值
思路
寻找排序数组的分治寻找,跟前面的螺旋类型很类似
代码
class Solution {
public:
int findMin(vector<int>& nums) {
int l,r,mid;
l=0;r=nums.size()-1;
while(l<=r)
{
mid=(r-l)/2+l;
if(nums[l]<=nums[r]) //一开始就排序好了,那就返回最左边的
return nums[l];
if(nums[l]<=nums[mid]) //如果左边单增,缩短左区间
l=mid+1;
else
r=mid; //如果不是左边单增,缩短右区间
}
return nums[r]; //返回
}
};
题目链接力扣
在排序数组中查找目标元素的第一个位置和最后一个位置
思路
我还是一个c++没学完的菜鸟啊,确实不太理解怎么返回两个数值,但是这个题本质是求找到一个数值第一次和最后一次出现的位置,注意这个是一个升序数组,所以用两个函数分别求出来第一次和最后一次的位置
向下取整不更新右边界会出现死循环,向上取整不更新左边界会出现死循环
代码
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int n = nums.size();
vector<int> ans(2, -1);
if (n == 0) return ans;
// 求第一次出现的位置
int l = 0, r = n - 1;
while (l < r) {
int mid = l + r >> 1; //向下取整
if (nums[mid] >= target) r = mid;
else l = mid + 1; //更新左循环
}
if (nums[r] != target) return ans;
ans[0] = r;
//求最后一次出现的位置
l = 0, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1; //向上取整
if (nums[mid] <= target) l = mid;
else r = mid - 1; //更新右循环
}
ans[1] = r;
return ans;
}
};
题目链接力扣
快速幂算法
思路
快速幂算法哦,只是刚好不用取余
之前写过关于快速幂算法的博客,大家可以自行学习一下,但是我还是认真的复习练习了一遍。这里确实是用到了二分的思想,不断除以2来减小时间复杂度,看我代码吧。快速幂不是很难的算法,但是很重要哦
代码
class Solution {
public:
double quickMul(double x, long long N) //原来leetcode可以定义函数
{
double sum=1.0;
while(N)
{
if(N%2==1)
sum*=x; //奇数就乘一个
x*=x;
N/=2;
}
return sum;
}
double myPow(double x, int n) {
long long N=n;
return N>0?quickMul(x,N):1/quickMul(x,-N); //负数情况倒数
}
};
题目链接力扣
有效的完全平方数
思路
倒也没啥,和前面的算术平方根很类似,我们就是要寻找在比m小的整数中有没有他的平方根,也是用到二分法一个一个来找哦