代码随想录 | 刷题记录 | Day01 | C++
Day 01 主要学习内容
- 二分法基本使用
- 二分法找左右边界
二分查找
1 找到就行 适用于没有重复数
/*
关键点:
1.while(l<r) or while(l<=r)
2.r or l == mid+1 or mid-1 or mid
3.如果为了防止mid太大的话可以选择 mid=l+(r-l)/2;
主要取决于边界,分为两种
第一种是左闭右闭,第二种是左闭右开
*/
//第一种
//左闭右闭 区间 l<=r 因为r==l时 [l,r]是合法的,只有一个数字 eg:[1,1] 区间内有一个元素为1
//判断是等于mid+1或者-1要看到底是不是l和r在你接下来搜索的区间内
//这个如果有重复元素的话有随机性,可能找到左边界可能找到右边界可能找到左右边界中间的某个数
int search(vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
while(l<=r)
{
int mid=l+r>>1;
if(nums[mid]>target) r=mid-1;
else if(nums[mid]<target) l=mid+1;
else return mid;
}
return -1;
}
//第二种
//左闭右开 l<r 因为l=r是 [l,r)无意义 eg:[1,1) 中没有元素 和上面的第一种类似
int search(vector<int>& nums, int target) {
int l=0,r=nums.size();
while(l<r)
{
int mid=l+r>>1;
if(nums[mid]>target) r=mid;//右开 所以r本来就不在区间内,那就可以等于mid而不用减1
else if(nums[mid]<target) l=mid+1;
else return mid;
}
return -1;
}
1 如果有重复数字 用以下方法寻找左右边界
第一种
//如果没有重复元素两种方式等价,并且l和r都相等,如果有重复元素,则第一种为左边界,第二种为右边界都是可以取到的
//循环结束条件均为l==r
void bsearch_1(int l, int r)//寻找左边界 区间左闭右开
{
while (l < r)
{
int mid = l + r >> 1;
if (nums[mid]>=target) r = mid;//找左边界往左边缩
else l = mid + 1;
}
l1 = l, r1 = r;
}
void bsearch_2(int l, int r)//寻找右边界 区间左开右闭
{
while (l < r)
{
int mid = l + r + 1 >> 1;
// +1 原因在于:通过 (l + r) / 2 计算mid的值,结果是向下取整的。 在区间内只有两个元素的时,r的值可以用l + 1代替,因此mid = (l + r) / 2 = (l + l + 1) / 2 = l。 这个时候更新l = mid,l的值更新后依旧为l。
if (nums[mid]<=target) l = mid;//找右边界往右边缩
else r = mid - 1;
}
l2 = l, r2 = r;
}
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> nums = { -1,0,3,5,5,5,5,5,9,12 };
int target = 5;
int l1, r1, l2, r2;
void bsearch_1(int l, int r)//寻找左边界 左闭右开
{
while (l < r)
{
int mid = l + r >> 1;
if (nums[mid]>=target) r = mid;
else l = mid + 1;
}
l1 = l, r1 = r;
}
void bsearch_2(int l, int r)//寻找右边界 左开右闭
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (nums[mid]<=target) l = mid;
else r = mid - 1;
}
l2 = l, r2 = r;
}
int main()
{
bsearch_1(0,10);
cout << l1<<" "<< r1 << endl;
bsearch_2(-1,9);
cout << l2<<" "<< r2 << endl;
return 0;
}
//输出:
//3 3
//7 7
/*做 力扣34时发现
如果nums为单独一个数或者两个相同的数且小于target 如nums={3,3} target=5;
左边界会找不到,l,r在循环结束后会等于nums.size()
如果nums为单独一个数或者两个相同的数且大于target 如nums={6,6} target=5;
右边界会找不到,l,r在循环结束后会等于-1
即 此时的 l r均为非法状态
第二种(建议使用)
//左右边界均使用左闭右闭来寻找 上面提到可能会有非法状态故用first和last保存合法结果
//见下面
//34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int first=-1,last=-1;
int l=0,r=nums.size()-1;
while(l<=r)//左边界
{
int mid=(l+r)>>1;
if(nums[mid]==target)
{
first=mid;//记录左边界
r=mid-1;
}
else if(nums[mid]>target)
r=mid-1;
else
l=mid+1;
}
l=0,r=nums.size()-1;
while(l<=r)//右边界
{
int mid=(l+r)>>1;
if(nums[mid]==target)
{
last=mid;//记录右边界
l=mid+1;
}
else if(nums[mid]>target)
r=mid-1;
else
l=mid+1;
}
return {first,last};
}
};
704.二分查找
模板任选其一即可
35.搜索插入位置
//找左边界的模板
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l=0,r=nums.size();
while(l<r)
{
int mid=(l+r)>>1;
if(nums[mid]>=target)
r=mid;
else
l=mid+1;
}
return l;
}
};
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int first=-1,last=-1;
int l=0,r=nums.size()-1;
while(l<=r)//左边界
{
int mid=(l+r)>>1;
if(nums[mid]==target)
{
first=mid;
r=mid-1;
}
else if(nums[mid]>target)
r=mid-1;
else
l=mid+1;
}
l=0,r=nums.size()-1;
while(l<=r)//右边界
{
int mid=(l+r)>>1;
if(nums[mid]==target)
{
last=mid;
l=mid+1;
}
else if(nums[mid]>target)
r=mid-1;
else
l=mid+1;
}
return {first,last};
}
};
代码随想录的解法也很棒放在这里了
69.x的平方根
class Solution {
public:
int mySqrt(int x) {
int l=0,r=x;
while(l<=r)
{
long long mid=(l+r)>>1;
if(mid*mid==x)
return mid;
else if(mid*mid>x)
r=mid-1;
else
l=mid+1;
}
return r;//找的其实是左边界,下界 返回r(个人认为)
}
};
367.有效的完全平方数
class Solution {
public:
bool isPerfectSquare(int num) {
int l=0,r=num;
while(l<=r)
{
long long mid=(l+r)/2;
if(mid*mid==num)
return true;
else if(mid*mid>num)
r=mid-1;
else
l=mid+1;
}
return false;
}
};