二分法:
时间复杂度:o(logn)
空间复杂度:o(1);
用二分法的条件:(1)数列是有界的;(2)数列具有单调性
二分法的模板:整型2种,浮点型1种;
整型:
1 | 3 | 5 | 7 | 8 |
找出3的下标:
当a[mid]<=num时,我们要在mid右边的区间里查找目标值,可是在mid这一点是有可能等于目标值num的,所以我们的l=mid,else r=mid-1;为了防止陷入死循环(假如l=4,r=5,(l+r)/2=4;),所以再次循环时mid=(l+r+1)/2;
#include <iostream>
using namespace std;
int a[100];
int main(){
int n,num;
cin>>n>>num;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l=1,r=n;
while(l<r){
int mid=(l+r+1)/2;
if(a[mid]<=num){
l=mid;
}else{
r=mid-1;
}
}
if(a[r]==num){
cout<<r<<endl;
}else{
cout<<"-1"<<endl;
}
return 0;
}
当a[mid]>=num时,我们要在mid左半边的区间里查找目标值num,因为右半边不包含我们的目标值,但在mid这一点是有可能等于num的,所以r=mid,else l=mid+1;
#include <iostream>
using namespace std;
int a[100];
int main(){
int n,num;
cin>>n>>num;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l=1,r=n;
while(l<r){
int mid=(l+r)/2;
if(a[mid]>=num){
r=mid;
}else{
l=mid+1;
}
}
if(a[r]==num){
cout<<r<<endl;
}else{
cout<<"-1"<<endl;
}
return 0;
}
浮点型:主要考虑精度的问题,只有一种写法
例1:
#include <iostream>
#include<algorithm>
#include<iomanip>
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
int n,m,c,i,j,t;
bool check(double k){
double sum=0,v=1;
for(j=1;j<=c;j++){
v*=(1+k);
sum+=(m/v);
}
return sum>=n;
}
int main(){
IOS;
cin>>n>>m>>c;
double l=0,r=20;
while(l+0.0001<r){
double mid=(l+r)/2;//这一步不可以用'>>',
if(check(mid)){//在 C++ 中,位运算符 >> 用于将一个整数的二进制表示向右移动指定的位数。
l=mid; //当对浮点数进行位运算时,编译器会将浮点数转换为整数,然后执行位运算
//因此,对浮点数进行位运算通常会导致不可预期的结果,因为浮点数的二进制表示通常不代表它的实际值。
}else{
r=mid;
}
}
cout<<fixed<<setprecision(1)<<l*100<<endl;
return 0;
}
例二:
#include <iostream>
#include<algorithm>
#include<iomanip>
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
int n,m,i,j;
double a[11000];
bool check(double k){
int sum=0;
for(j=1;j<=n;j++){
sum+=(a[j]/k);
}
return sum>=m;
}
int main(){
IOS;
cin>>n>>m;
double sum=0.0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum=max(sum,a[i]);
}
double l=0.0,r=sum;
while(r-l>1e-6){
double mid=(l+r)/2;
if(check(mid)){
l=mid;
}else{
r=mid;
}
}
cout<<l<<endl;//cout默认情况下输出浮点型小数时
//保留六位有效数字
return 0;
}
关于STL中的lower_bound和upper_bound :
1>>它俩的本质也是二分
2>>是在有序数列中进行查找的函数
(有序序列是指其中的元素按照某种规则或者特定的顺序排列的序列)
lower_bound:
返回在一个范围[first,last]中第一个大于或等于给定值的迭代器
upper_bound:
返回第一个大于给定值的迭代器
lower_bound和upper_bound 的相同点:
如果给定值不存在,则返回第一个大于该值元素的迭代器
如果所有元素都小于给定值,则返回末尾元素的迭代器
关于lower_bound的实例:
如果是vector:
vector<int> vec = {1, 2, 3, 3, 5, 7, 9};
auto lb = lower_bound(vec.begin(), vec.end(), 3);
如果是set:
set<int> mySet = {1, 2, 3, 5, 7, 9};
auto it = mySet.lower_bound(3);
如果是map:
map<int, string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}, {5, "five"}, {7, "seven"}};
auto it = myMap.lower_bound(3);
如果是arr[]数组,里面有n个元素:
int it = lower_bound(arr, arr + n, 3)-arr;
当lower_bound和upper_bound相减时,得到的值是有多少个相同的给定值:
auto lower1 = distance(vec.begin(),lower_bound(vec.begin(), vec.end(), 3));
auto upper1 = distance(vec.begin(),upper_bound(vec.begin(), vec.end(), 3));
// 计算范围内元素的个数
cout<<upper1-lower1<<endl;
auto:
是 C++11 引入的关键字,用于自动推导变量的类型。使用 auto
关键字声明变量时,编译器会根据变量的初始化表达式推断出其类型,并将其替换为实际类型。这样做的好处是可以简化代码,特别是在处理模板和迭代器等复杂类型时。
auto x = 5; // x的类型将会是int
auto y = 3.14; // y的类型将会是double
vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin(); // it的类型将会是vector<int>iterator