二分查找的几个变种(Java代码实现)
2015-10-31 15:30
633 查看
二分查找是一种比较普遍的查找方法,面试的时候也会经常问道,但稍微有点档次的公司都不会直接问二分查找的递归和非递归实现(因为太没技术含量了),所以二分查找的几个变种,面试这还是需要熟悉的,下面介绍几种二分查找的变种:
1.找s的最小位置
3.找小于等于s的最大位置
题目1:假设a和b都是升序的,分别有n1和n2个元素,求两个数组合并后第k大元素值。
下面,我们先来分析一个类似的问题,假设a和b都是升序的,分别有n1和n2个元素,求两个数组合并后第k大元素值。
分别取两个数组中间索引的数,a[x]和b[y],比较两个数的大小:
if( a[x] <= a[y] )
——————————————————————————————————————————————————————————————
如果k <= x+y+1,则可以判断出b[y]以及b[y]后面的元素都可以排除在外,减小搜索规模。
如果k > x+y+1,则可以判断出a数组的前半部分元素都不符合条件,减少a一半的搜索规模。
该算法利用了递归
的思想,结束条件是:
a中元素排除出去,则选择b中得第k大元素;
b中元素全部排除,选择a中第k大元素。
——————————————————————————————————————————————————————————————
代码实现:
题目2:找到轮转后的有序数组中第k小的数
对于普通的有序数组来说,这个问题是非常简单的,因为数组中的第k-1个数(即A[k-1])就是所要找的数,时间复杂度是O(1)常量。但是对于轮转后的有序数组,在不知道轮转的偏移位置,我们就没有办法快速定位第K个数了。
不过我们还是可以通过二分查找法,在log(n)的时间内找到最小数的在数组中的位置,然后通过偏移来快速定位任意第k个数。当然此处还是假设数组中没有相同的数,原排列顺序是递增排列。
在轮转后的有序数组中查找最小数的算法如下:
接着基于此结果进行偏移,再基于数组长度对偏移后的值取模,就可以找到第K个数在数组中的位置了:
当然,二分查找的变种肯定不止这些,我只是总结了几个经典的,以后会继续补充,其实算法万变不离其宗,只要你能吃透这个算法,任他怎么变你也依然能够谈笑风生。
1.找s的最小位置
int BinarySearch(int a[],int l,int r) { int m,ans=-1; while(l<=r) { m=(l+r)/2; //满足条件 if(a[m]==s) ans=m,r=m-1; else l=m+1; } return ans; }2.找s的最大位置
int BinarySearch(int a[],int l,int r) { int m,ans=-1; while(l<=r) { m=(l+r)/2; //满足条件 if(a[m]==s) ans=m,l=m+1; else r=m-1; } return ans; }
3.找小于等于s的最大位置
int BinarySearch(int a[],int l,int r,int key) { int m,ans=-1; while(l<=r) { m=(l+r)/2; if(s>=a[m]) ans=m,l=m+1; else r=m-1; } return ans;OK,以上都是属于简单变形,一般的面试者都能应对,那接下来的变种就不是那么好看喽:
题目1:假设a和b都是升序的,分别有n1和n2个元素,求两个数组合并后第k大元素值。
下面,我们先来分析一个类似的问题,假设a和b都是升序的,分别有n1和n2个元素,求两个数组合并后第k大元素值。
分别取两个数组中间索引的数,a[x]和b[y],比较两个数的大小:
if( a[x] <= a[y] )
——————————————————————————————————————————————————————————————
如果k <= x+y+1,则可以判断出b[y]以及b[y]后面的元素都可以排除在外,减小搜索规模。
如果k > x+y+1,则可以判断出a数组的前半部分元素都不符合条件,减少a一半的搜索规模。
该算法利用了递归
的思想,结束条件是:
a中元素排除出去,则选择b中得第k大元素;
b中元素全部排除,选择a中第k大元素。
——————————————————————————————————————————————————————————————
代码实现:
int getMedian( int a[],int b[],int s1, int n1, int s2, int n2, int k ) { //x和y分别记录中间值的索引 int x, y; x = (s1 + n1) / 2; //记录a的中位数索引 y = (s2 + n2) / 2; //记录b的中位数索引 if( s1 > n1 ) return b[s2+k-1]; if( s2 > n2 ) return a[s1+k-1]; if( a[x] <= b[y] ) { if( k <= (x-s1) + (y-s2) + 1 ) { return getMedian( s1, n1, s2, y-1, k ); } else { return getMedian( x+1, n1, s2, n2, k-(x-s1)-1 ); } } else { if( k <= (x-s1)+(y-s2)+1 ) { return getMedian( s1, x-1, s2, n2, k ); } else { return getMedian( s1, n1, y+1, n2, k-(y-s2)-1 ); } } return 0; }
题目2:找到轮转后的有序数组中第k小的数
对于普通的有序数组来说,这个问题是非常简单的,因为数组中的第k-1个数(即A[k-1])就是所要找的数,时间复杂度是O(1)常量。但是对于轮转后的有序数组,在不知道轮转的偏移位置,我们就没有办法快速定位第K个数了。
不过我们还是可以通过二分查找法,在log(n)的时间内找到最小数的在数组中的位置,然后通过偏移来快速定位任意第k个数。当然此处还是假设数组中没有相同的数,原排列顺序是递增排列。
在轮转后的有序数组中查找最小数的算法如下:
int findIndexOfMinVaule(int A[], int low, int high) { if (low > high) return -1; while (low < high) { int mid = (low + high)/2; if (A[mid] > A[high]) low = mid +1; else high = mid; } return low; }
接着基于此结果进行偏移,再基于数组长度对偏移后的值取模,就可以找到第K个数在数组中的位置了:
int findKthElement(int A[], int m, int k) { if (k > m) return -1; int base = findIndexOfMinVaule(A, 0, m-1); int index = (base+k-1)%m; return index; }
当然,二分查找的变种肯定不止这些,我只是总结了几个经典的,以后会继续补充,其实算法万变不离其宗,只要你能吃透这个算法,任他怎么变你也依然能够谈笑风生。
相关文章推荐
- maven编译成功 eclipse现实语法错误
- Java中Runable和Thear区别
- SpringMVC中为什么已经作废了session, 在另一个jsp中仍然能取到session中的值?
- SpringMVC + servlet3.0 文件上传的配置和实现
- SpringMVC 全局异常处理代码
- java多线程
- java 调用webservice 报 No service named XXXXHttpSoap11Endpoint is availab异常
- java中给出一个不多于5位数的正整数,要求,第一是求出它是第多少位的,逆序打印出各位数字
- jdk目录结构
- spring-security 3.2.8 配置及部分源码分析
- Java异常处理
- java中猴子吃桃子的问题和求分数序列和的问题
- java通过JDBC链接SQLServer2012
- bat启动java程序,集成jre
- Spring4:JDBC
- java数组转json
- [Android]反编译apk + eclipse中调试smali
- java1.8--OptionalInt,OptionalDouble,OptionalLong类
- java1.8--OptionalInt,OptionalDouble,OptionalLong类
- java IO流总结