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

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

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



当然,二分查找的变种肯定不止这些,我只是总结了几个经典的,以后会继续补充,其实算法万变不离其宗,只要你能吃透这个算法,任他怎么变你也依然能够谈笑风生。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值