二分模板
int[] arr = {1,2,3,4,5,5,5,6,7,8,9};
具体情况,具体调试,不要死记硬背
找左边第一个
mid = (le + re) >> 1
le = mid + 1
re = mid
找左边最后一个
mid = (le + re + 1) >> 1
le = mid
re = mid - 1
package ch04.bisection;
/**
* @author Dylan
* @date 2020/5/2 - 17:14
*/
public class Template {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,5,5,6,7,8,9};
System.out.println(binarySearch1(arr, 5)); // 7
System.out.println(binarySearch2(arr, 5)); // 4
System.out.println(binarySearch3(arr, 5)); // 6
System.out.println(binarySearch4(arr, 5)); // 3
}
// 找到第一个大于x的下标
public static int binarySearch1(int[] arr, int x){
int le = 0, re = arr.length - 1;
while(le < re){
int mid = (le + re) >> 1;
if(arr[mid] <= x) le = mid + 1;
else re = mid;
}
return le;
}
// 找到左边第一个等于x的下标
public static int binarySearch2(int[] arr, int x){
int le = 0, re = arr.length - 1;
while(le < re){
int mid = (le + re) >> 1;
if(arr[mid] < x) le = mid + 1;
else re = mid;
}
return le;
}
//找到左边最后一个等于x的下标
public static int binarySearch3(int[] arr, int x){
int le = 0, re = arr.length - 1;
while(le < re){
int mid = (le + re + 1) >> 1;
if(arr[mid] <= x) le = mid;
else re = mid - 1;
}
return le;
}
//找到左边第一个小于x的下标
public static int binarySearch4(int[] arr, int x){
int le = 0, re = arr.length - 1;
while(le < re){
int mid = (le + re + 1) >> 1;
if(arr[mid] < x) le = mid;
else re = mid - 1;
}
return le;
}
}
二分法求函数零点
递归写法
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
double f(double x){
return x*x*x - x;
}
double Find_Zero_Point(double left, double right, double precesion){
if(right - left < precesion) return left;
double mid = (left + right) / 2;
if(abs(f(mid))< 1e-5) return mid;
if(f(mid) * f(right) > 0) return Find_Zero_Point(left, mid, precesion);
else return Find_Zero_Point(mid, right, precesion);
}
int main(){
double ans = Find_Zero_Point(-1.7, -0.6, 0.001);
cout<<ans<<endl;
return 0;
}
非递归写法
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
double f(double x){
return x*x*x - x;
}
double Find_Zero_Point(double left, double right, double precesion){
while(right - left > precesion){
double mid = (left + right) / 2;
if(abs(f(mid))< 1e-5) return mid;
if(f(mid) * f(right) > 0) right = mid;
else left = mid;
}
return left;
}
int main(){
double ans = Find_Zero_Point(-1.7, -0.6, 0.001);
cout<<ans<<endl;
return 0;
}
STL的二分查找
在从小到大的排序数组中,
-
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
-
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
在从大到小的排序数组中,重载lower_bound()和upper_bound()
-
lower_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
-
upper_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
int cmd(int a,int b){
return a>b;
}
int main(){
int num[6]={1,2,7,7,15,34};
sort(num,num+6); //按从小到大排序
int pos1=lower_bound(num,num+6,7)-num; //返回数组中第一个大于或等于被查数的值
int pos2=upper_bound(num,num+6,7)-num; //返回数组中第一个大于被查数的值
cout<<pos1<<" "<<num[pos1]<<endl;
cout<<pos2<<" "<<num[pos2]<<endl;
sort(num,num+6,cmd); //按从大到小排序
int pos3=lower_bound(num,num+6,7,greater<int>())-num; //返回数组中第一个小于或等于被查数的值
int pos4=upper_bound(num,num+6,7,greater<int>())-num; //返回数组中第一个小于被查数的值
cout<<pos3<<" "<<num[pos3]<<endl;
cout<<pos4<<" "<<num[pos4]<<endl;
return 0;
}
二分答案题目的特征
通常来说,题面中有,或者暗含类似于:
- 最大值最小
- 最小值最大
- 最靠近某个值
- 最小的能满足条件的代价