今天搜狐笔试,有一道算法题:给定排好序的成绩数组和及格分数线,要求找出及格的人数。
看到这个题就知道有坑,及格分数线的分数不一定出现在数组之中,而且分数还有可能有重复的,最普通的二分查找法根本没办法处理这种情况,最后这题答得不怎么样。
镇定思痛,看到一个还不错的总结:http://www.cnblogs.com/ider/archive/2012/04/01/binary_search.html
这里我也顺着思路来过一遍。
先说最普通的二分查找:
public static int bsearchWithoutRecursion(int array[], int low, int high, int target)
{
while(low <= high)
{
int mid = (low + high)/2;
if (array[mid] > target)
high = mid - 1;
else if (array[mid] < target)
low = mid + 1;
else //find the target
return mid;
}
//the array does not contain the target
return -1;
}
估计很多人都只会这种二分,这种二分只能查找出目标数存在于数组之中的情况。
下面介绍两种求最靠近目标数的上界和下界的二分查找方法:
public static int BSearchLowerBound(int array[], int low, int high, int target)
{
//Array is empty or target is less than any every element in array
if(high < low || target <= array[low]) return -1;
int mid = (low + high + 1) / 2; //make mid lean to large side
while (low < high)
{
if (array[mid] < target) //需要寻找的array[mid] 比 target小
low = mid;
else
high = mid - 1; //此时array[mid] >= target,mid不可能是最终结果,所以这里mid -1
mid = (low + high + 1) / 2;
}
return array[mid];
}
首先是求下界,在求上下界的时候要特别注意什么时候等于mid ,什么时候等于mid +- 1,其实理解了这个记起来还是很方便的,另外求下界的时候mid = ( low + high + 1) / 2,如果没有+1,在while里面可能就会出现死循环,比如low = 4 ,high = 5 ,然后判断时走 low = mid分支。
下面是求上界的二分查找方法:
public static int BSearchUpperBound(int array[], int low, int high, int target)
{
//Array is empty or target is larger than any every element in array
if(low > high || target >= array[high]) return -1;
int mid = (low + high) / 2;
while (high > low)
{
if (array[mid] > target)
high = mid;
else
low = mid + 1;
mid = (low + high) / 2;
}
return mid;
}
这里就不详细赘述了,原理和求上界类似。
今天笔试的这个题目其实求一次下界就可以做出来,掌握了这些以后才发现问题远没有自己考虑的那么麻烦!还是要好好修炼。
有些题目需要求出在某个区间[a,b]的数字个数,可以对a求下界,对b求上界,求出的两个mid值之差再减一就是结果,不过要考虑两个都返回 -1 index 的情况。