跑深度学习的时候也没事干 刷刷题
二分学了好多次,每次都是学了忘忘了又学,发个博客当笔记了
查找某个具体数的模板,这肯定是学二分时的第一个模板,没什么好说的
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0;
int r = nums.size()-1;
while(l<=r){
int mid = l+(r-l)/2;
if(nums[mid]==target){
return mid;
}else if(nums[mid]<target){
l = mid+1;
}else{
r = mid-1;
}
}
return -1;
}
};
但是问题复杂时,这套方法就不灵了,每次看懂了,过段时间又忘了,所以总结一下
二分搜索解题两大原则1.每次都要缩减搜索区域2.每次缩减不能排除潜在答案
三大模板
1.找一个准确值
循环条件:l<=r
缩减搜索空间:l = mid+1,r = mid-1
2.找一个模糊值(比4大的数)
循环条件:l<r
缩减搜索空间:l=mid,r=mid-1或者l=mid+1,r=mid
3.万用形
循环条件:l<r-1
缩减搜索空间:l=mid,r=mid
leecode278.第一个错误的版本
实际上就是找出第一个出错的版本,出错版本后的版本一定出错
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
int l = 1;
int r = n;
while(l<r){
int mid = l + (r-l)/2;
if(isBadVersion(mid)){
r = mid;
}else{
l = mid+1;
}
}
return l;
}
};
思路:因为每次都要缩减搜索空间,所以当isBadVersion返回true时,他的右边一定都为出错版本,左边还有可能有出错版本,他自己也可能是第一个出错的版本,所以r=mid,不可以排除潜在答案。而当isBadVersion返回false时,这个版本一定为正确版本,此时可以排除这个答案所以,l=mid+1
leecode410.分割数组最大值
给一个非负整数数组和一个整数m,将数组分成连续的m个非空连续数组,使得这n个连续数组的和的最大值最小。输出这个最小的最大和。
class Solution {
public:
bool split(vector<int>& nums,int sum,int k){
int temp_sum=0;
int count = 1;
for(int i=0;i<nums.size();i++){
if(temp_sum + nums[i]>sum){
count++;
temp_sum =nums[i];
}else{
temp_sum +=nums[i];
}
}
return count<=k;
}
int splitArray(vector<int>& nums, int k) {
int sum=0;
int maxx=0;
for(int i=0;i<nums.size();i++){
sum+=nums[i];
maxx = max(maxx,nums[i]);
}
int l=maxx;
int r=sum;
while(l<r){
int mid = (l+r)>>1;
if(split(nums,mid,k)){
r = mid;
}else{
l =mid+1;
}
}
return l;
}
};
这道题转换思路,实际上是对要求的最大和进行二分。split返回true时,分出的数组小于或等于要求的m,此时不可以排除潜在答案,所以r=mid。split返回false时,分出的数组大于要求的m,证明我们的最大和过小,并且此时不可能为最后的解,l=mid+1.
leecode852.山脉数组的峰顶索引
class Solution {
public:
int peak(vector<int>& arr,int n){
if(n==0){
return 2;
}
if(n==arr.size()-1){
return -2;
}
if(arr[n-1]<arr[n] && arr[n]<arr[n+1]){
return 2;
}
if(arr[n-1]>arr[n] && arr[n]>arr[n+1]){
return -2;
}
if(arr[n-1]<arr[n] && arr[n]>arr[n+1]){
return 1;
}
return 0;
}
int peakIndexInMountainArray(vector<int>& arr) {
int l=0;
int r=arr.size()-1;
int mid;
while(l<=r){
mid=l+(r-l)/2;
if(peak(arr,mid)==1){
return mid;
}
if(peak(arr,mid)==2){
l=mid+1;
}
if(peak(arr,mid)==-2){
r=mid-1;
}
}
return mid;
}
};
这题还挺简单的,是精确搜索,返回1代表找到了,返回2代表要往右寻找,返回-2代表要往左寻找,但是我写麻烦了,简便写法如下
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int l=1;
int r=arr.size()-2;
int mid=0;
while(l<r){
mid=(l+r)/2;
if(arr[mid]<arr[mid+1]){
l=mid+1;
}else{
r=mid;
}
}
return l;
}
};
可视为模糊查找,查找第一个arr[mid]>arr[mid+1]的数,而n == 0 || n == arr.size()-1的情况直接被排除了,不可能为峰顶元素。
leecode1292.元素和小于等于阈值的正方形的最大边长
二分答案结合二维前缀和
class Solution {
public:
int maxSideLength(vector<vector<int>>& mat, int threshold) {
int m = mat.size();
int n = mat[0].size();
vector<vector<int>> summ(m + 1, vector<int>(n + 1));
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
summ[i][j]=summ[i-1][j]+summ[i][j-1]-summ[i-1][j-1]+mat[i-1][j-1];
// cout<<summ[i][j]<<endl;
}
}
// return maxMat(mat,threshold,2);
int l = 0;
int r = min(mat.size(),mat[0].size());
int mid;
int sum;
while(l<r){
mid = l+(r-l+1)/2;
int flag=0;
for(int i=0;i+mid<=mat.size();i++){
for(int j=0;j+mid<=mat[0].size();j++){
sum = summ[i+mid][j+mid]-summ[i][j+mid]-summ[i+mid][j]+summ[i][j];
if(sum<=threshold){
flag=1;
}
}
}
sum=0;
if(flag){
l=mid;
}else{
r=mid-1;
}
}
return l;
}
};
文章借鉴https://www.bilibili.com/video/BV1Ng4y1q7E3/?spm_id_from=333.337.search-card.all.click&vd_source=7ff09f6ed463c54f8768c5148846a8e5