二分查找写法总结
来源:互联网 发布:照片模板软件 编辑:程序博客网 时间:2024/06/06 05:57
二分查找,是通过二分区间不断的缩小查找范围,已达到快速查找的目的。在ACM中,二分查找经常穿插在各类题型中,用途很广泛,是必须要掌握的一个算法。
二分查找的对象
一般二分查找都是对有单调性的序列操作,序列的数据类型包括整型和浮点型
整型
- 常用写法分类
- 取整方式:向下取整、向上取整
- 区间开闭:闭区间、左闭右开区间、左开右闭区间、开区间
- 问题类型:
- 对于不下降序列a,求最小的i,使得a[i] = key,即查找序列中等于key值的第一个位置,等同于c++里的lower_bound;
- 对于不下降序列a,求最大的i,使得a[i] = key,即查找序列中等于key值的最后一个位置,等同于c++里的upper_bound;
- 对于不下降序列a,求最小的i,使得a[i] > key,即最小化最大值;
- 对于不下降序列a,求最大的i,使得a[i] < key,即最大化最小值;
- 对于不下降序列a,求最小的i,使得a[i] >= key;
- 对于不下降序列a,求最大的i,使得a[i] <= key;
- 对于不上升序列a,求最小的i,使得a[i] = key
- 对于不上升序列a,求最大的i,使得a[i] = key
- 对于不上升序列a,求最小的i,使得a[i] > key
- 对于不上升序列a,求最大的i,使得a[i] < key
- 对于不上升序列a,求最小的i,使得a[i] >= key;
- 对于不上升序列a,求最大的i,使得a[i] <= key;
一般会先用sort排序预处理序列,默认升序,因此这里只列举前种写法。
- 常用写法分类
对于不下降序列a,求最小的i,使得a[i] = key,即查找序列中等于key值的第一个位置,等同于c++里的lower_bound
//找第一个等于key的位置int Binary_search1(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<key) l=mid+1; else r=mid; } if(a[r]==key) return r;//在区间右端点终止 return -1;//不存在}
- 对于不下降序列a,求最大的i,使得a[i] = key,即查找序列中等于key值的最后一个位置,等同于c++里的upper_bound
//找最后一个等于key的位置int Binary_search2(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r+1-l)>>1);//向上取整 if(a[mid]<=key) l=mid; else r=mid-1; } if(a[l]==key) return l;//在区间左端点终止 return -1;//不存在}
- 对于不下降序列a,求最小的i,使得a[i] > key,即最小化最大值
//找第一个大于key的位置int Binary_search3(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<=key) l=mid+1; else r=mid; } if(a[r]>key) return r;//在区间右端点终止 return -1;//不存在}
- 对于不下降序列a,求最大的i,使得a[i] < key,即最大化最小值
//找最后一个小于key的位置int Binary_search4(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r+1-l)>>1);//向上取整 if(a[mid]<key) l=mid; else r=mid-1; } if(a[l]<key) return l;//在区间左端点终止 return -1;//不存在}
- 对于不下降序列a,求最小的i,使得a[i] >= key
//找第一个大于或等于key的位置int Binary_search5(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<key) l=mid+1; else r=mid; } if(a[r]>=key) return r;//在区间右端点终止 return -1;//不存在}
- 对于不下降序列a,求最大的i,使得a[i] <= key
//找最后一个小于等于key的位置int Binary_search6(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r+1-l)>>1);//向上取整 if(a[mid]<=key) l=mid; else r=mid-1; } if(a[l]<=key) return l;//在区间左端点终止 return -1;//不存在}
- 更保险的写法,不用管最后是取左值还是右值,推荐使用!
例如:对于不下降序列a,求最小的i,使得a[i] >= key,即查找序列中大于或等于key值的第一个位置,等同于c++里的lower_bound
//找第一个大于或等于key的位置int Binary_search7(int a[],int n,int key){ int mid,l=0,r=n-1,ans=-1;//区间[0,n-1] while(l<=r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<key) l=mid+1; else { ans=mid;//不断向下更新 r=mid-1; } } return ans;}
- 对于不下降序列a,求最大的i,使得a[i] <= key
//找最后一个小于或等于key的位置int Binary_search8(int a[],int n,int key){ int mid,l=0,r=n-1,ans=-1;//区间[0,n-1] while(l<=r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<=key) { ans=mid;//不断向上更新 l=mid+1; } else r=mid-1; } return ans;}
浮点型
一般用于使结果达到某一精度而不断地进行二分,也有两种写法,一种是指定循环次数作为终止条件,1次循环可以把区间的范围缩小一半,100次的循环则可以达到2−100≈10−30 的精度范围,这样做应该是没有问题的,而且不会陷入无限循环;另一种是指定精度为eps作为终止条件,但是这样做需要注意,如果eps取得太小了,就有可能因为浮点小数精度的原因导致陷入死循环。指定精度
#define eps 1e-8double Binary_search(double l, double r){ double mid; while (r-l>=eps) { mid=(l+r)/2.0; if(judge(mid)) r=mid; else l=mid; } return mid;}
- 指定循环次数
double Binary_search(double l, double r){ double mid; for(int i=0; i<100; i++) { mid=(l+r)/2.0; if(judge(mid)) r=mid; else l=mid; } return mid;}
实际测试一下
上代码:
#include<iostream>#include<cstdio>using namespace std;//找第一个等于key的位置int Binary_search1(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<key) l=mid+1; else r=mid; } if(a[r]==key) return r;//在区间右端点终止 return -1;//不存在}//找最后一个等于key的位置int Binary_search2(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r+1-l)>>1);//向上取整 if(a[mid]<=key) l=mid; else r=mid-1; } if(a[l]==key) return l;//在区间左端点终止 return -1;//不存在}//找第一个大于key的位置int Binary_search3(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<=key) l=mid+1; else r=mid; } if(a[r]>key) return r;//在区间右端点终止 return -1;//不存在}//找最后一个小于key的位置int Binary_search4(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r+1-l)>>1);//向上取整 if(a[mid]<key) l=mid; else r=mid-1; } if(a[l]<key) return l;//在区间左端点终止 return -1;//不存在}//找第一个大于或等于key的位置int Binary_search5(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<key) l=mid+1; else r=mid; } if(a[r]>=key) return r;//在区间右端点终止 return -1;//不存在}//找最后一个小于等于key的位置int Binary_search6(int a[],int n,int key){ int mid,l=0,r=n-1;//区间[0,n-1] while(l<r) { mid=l+((r+1-l)>>1);//向上取整 if(a[mid]<=key) l=mid; else r=mid-1; } if(a[l]<=key) return l;//在区间左端点终止 return -1;//不存在}//找第一个大于或等于key的位置int Binary_search7(int a[],int n,int key){ int mid,l=0,r=n-1,ans=-1;//区间[0,n-1] while(l<=r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<key) l=mid+1; else { ans=mid;//不断向下更新 r=mid-1; } } return ans;}//找最后一个小于或等于key的位置int Binary_search8(int a[],int n,int key){ int mid,l=0,r=n-1,ans=-1;//区间[0,n-1] while(l<=r) { mid=l+((r-l)>>1);//向下取整 if(a[mid]<=key) { ans=mid;//不断向上更新 l=mid+1; } else r=mid-1; } return ans;}int main(){ int a[]={1, 1, 2, 2, 4, 4, 8, 8, 10, 10}; int n=10; int key=8; cout<<"下标:"; for(int i=0; i<n; i++) cout<<" "<<i; cout<<endl<<"序列:"; for(int i=0; i<n; i++) cout<<" "<<a[i]; cout<<endl; cout<<"第一个等于"<<key<<"的位置:"<<Binary_search1(a,n,key)<<endl; cout<<"最后一个等于"<<key<<"的位置:"<<Binary_search2(a,n,key)<<endl; cout<<"第一个大于"<<key<<"的位置:"<<Binary_search3(a,n,key)<<endl; cout<<"最后一个小于"<<key<<"的位置:"<<Binary_search4(a,n,key)<<endl; cout<<"第一个大于或等于"<<key<<"的位置:"<<Binary_search5(a,n,key)<<endl; cout<<"最后一个小于或等于"<<key<<"的位置:"<<Binary_search6(a,n,key)<<endl; cout<<"第一个大于或等于"<<key<<"的位置:"<<Binary_search7(a,n,key)<<endl; cout<<"最后一个小于或等于"<<key<<"的位置:"<<Binary_search8(a,n,key)<<endl; return 0;}
测试结果:
总结一下
二分有几个需要注意的地方:
1. 数列中相同值的处理;
2. 循环不变式的终止条件;
3. 边界条件的判定。
写法很多,建议只记一种,用的时候灵活变通就好。
阅读全文
1 0
- 二分查找写法总结
- 二分查找的正确写法
- 关于二分查找的写法
- 二分查找总结
- 二分查找的总结
- 【二分查找】学习总结
- 二分查找分类总结
- 二分查找小小总结
- 二分查找总结
- 二分查找总结
- 二分查找总结
- 二分查找自我总结
- 二分查找分类总结
- LeetCode二分查找总结
- 二分查找总结
- 二分查找总结
- 二分查找总结
- binarySearch二分查找总结
- 利用支持向量机(SVM)做手写数字识别
- linux常用命令(1)
- Myeclipse导入项目注释乱码问题的解决
- Java 国王放麦子
- 线性规划——单纯型算法
- 二分查找写法总结
- WordPress中函数钩子hook的作用及基本用法
- Stack
- Less
- HDU 2896 病毒侵袭——AC自动机
- 一句话木马绕过和防御
- 序列化的基本操作
- 常见数据类型之间的转换
- AtCoder Grand Contest 019 B