先附个经典二分搜索代码
int BinarySearch(int arr[],int N,int target) { int p=0; int r=N-1; while(p<=r){ int q=r-(r-p)/2; if(arr[q]==target) return q; else if(arr[q]<target) p=q+1; else r=q; } return -1; }
1.考察要点
1.二分搜索不仅在有序序列中可以运用,无序序列也同样适用,只要满足每次查找都会缩小一半查找范围。
2.注意循环初始条件,边界值,划分点的处理,确保循环不会无法终止。
3.划分点的经典写法:(p+r)/2 ,安全写法:r-(r-p)/2或p+(r-p)/2
2.局部最小值问题
问题描述:在一个无序序列中,且任意两个相邻元素都不相等的情况下,查找满足arr[i]<arr[i+1]且arr[i]<arr[i-1]的元素。
查找方法:
1.arr为空或长度为1时返回。
2.判断两端点的情况
如果端点值中有一个端点是局部最小值,则立即返回。
3.端点两边都不是局部最小,如下,讨论中间点mid。
4.中间点的情况
1.当属于情况2时,直接返回mid.
2.否则,如3的两边,4的左边,比存在局部最小值,因为相邻元素不同,以4的左边为例,
在mid的左边,一旦出现上升点,则此点局部最小,否则若一直向左降序,则arr[1]为局部最小值。
3.未画出的情况5参考情况4。
附一个查找局部最小元素的代码
int BinarySearch2(int arr[],int p,int r) { //if(p>r) return -1; if(p==r) return p; if(arr[p]<arr[p+1]) return p; if(arr[r]<arr[r-1]) return r; int q=r-(r-p)/2; if(arr[q]<arr[q-1]){ if(arr[q]<arr[q+1]) return q; return BinarySearch2(arr,q+1,r); } else{ return BinarySearch2(arr,p,q-1); } }
3.含有重复元素的有序数组问题
问题描述:给定有序数组arr,num,查找num在arr中出现的最左的位置。
举例分析:num=3,arr={1,2,3,3,4,4,4,4,4}
1.初始状态:res=-1,arr={1,2,3,3,4,4,4,4,4}
2.mid=4,arr={1,2,3,3},res=-1;
3.mid=2,arr={3,3},res=-1;
4.mid=3左,arr={},返回res=2.
总结:维护一个res,当找到num时,继续向左找,直到要找部分为空。
附一个查找最右元素的代码
int BinarySearch1(int arr[],int p,int r,int target,int res) { if(p>r) return res; int q=(p+r)/2; if(arr[q]==target) return BinarySearch1(arr,q+1,r,target,q); else if(arr[q]<target) return BinarySearch1(arr,q+1,r,target,res); else return BinarySearch1(arr,p,q-1,target,res); }
4.求有序循环序列的最小元素
对于数组L.....R,如{4,5,6,0,1,2,2,3}
1.如果arr[L]<arr[R],则返回arr[L]
2.如果arr[M]>arr[R],则最小值位于M...R
3.如果arr[M]<arr[L],则最小值位于L...R
4.如果arr[L]<=arr[M]<=arr[R],又arr[L]>=arr[R],故arr[L]=arr[R]=arr[M],此时只能遍历求得最小值。
5.由于划分元素不会被裁去,需要重新处理边界值问题(此题当元素个数为2时继续循环不会减少)。
附一个查找最小元素的代码
int BinarySearch3(int arr[],int N) { int p=0; int r=N-1; while(1){ if(r-p == 1) return arr[r]; if(arr[p]<arr[r]) return arr[p]; int q=r-(r-p)/2; if(arr[q]<arr[p]) r=q; else if(arr[q]>arr[r]) p=q; else{ int m=arr[p]; for(int i=p+1;i<=r;i++) m=m<arr[i]?m:arr[i]; return m; } } return -1; }
5.求有序无重复元素的最左原位
1.定义:最左原位:满足arr[i]=i的最左位置i
2.步骤:
初始化res=-1
1.如果arr[0]>N-1或arr[N-1]<0,返回-1
2.如果arr[M]<M,则左边排除,搜索0...M-1(这是因为数组有序且不重复,故左半部分最低与i减少速度相同,故arr[i]恒小于i
如果arr[M]>M,则右边排除,搜索M+1...N-1
3.如果arr[M]=M,更新res,由于找最左原位,故继续向左找
4.重复1-3,直到待查找序列空。
附一个查找最左原位
int BinarySearch4(int arr[],int N) { int res=-1; int p=0; int r=N-1; while(p<=r){ int q=r-(r-p)/2; if(arr[p]>r) return res; if(arr[r]<p) return res; else if(arr[q]>q) r=q-1; else if(arr[q]<q) p=q+1; else { res=q; r=q-1; } } return res; }