您的位置:首页 > 编程语言 > Java开发

二分查找的几个变种(Java代码实现)

2015-10-31 15:30 633 查看
二分查找是一种比较普遍的查找方法,面试的时候也会经常问道,但稍微有点档次的公司都不会直接问二分查找的递归和非递归实现(因为太没技术含量了),所以二分查找的几个变种,面试这还是需要熟悉的,下面介绍几种二分查找的变种:

  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;
}


当然,二分查找的变种肯定不止这些,我只是总结了几个经典的,以后会继续补充,其实算法万变不离其宗,只要你能吃透这个算法,任他怎么变你也依然能够谈笑风生。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: