折半查找的一些注意事项

int search0(int *vec, int N, int key)
{
	int lo = 0, hi = N - 1, mid;

	while (lo <= hi) {			// 注意点1
		mid = lo + (hi - lo) / 2;// 注意点2
		if (key == vec[mid])
			return mid;
		else if (key < vec[mid])
			hi = mid - 1;		// 注意点3
		else
			lo = mid + 1;		// 注意点3
	}

	return -1;
}

    注意点1:不能用lo < hi做判断条件,因为无法处理区间只包含一个元素的情况。出现这种情况,可能的原因有两种,一是原始序列就只包含一个元素,二是在逐步逼近的过程中,产生了只包含一个元素的区间。

    注意点2:这个用法比mid=(lo+hi)/2安全。

    注意点3:每次进入while循环,搜索的是[lo,hi]这个双闭区间。当本次未找到,则说明mid这个位置已经被搜索过,是不符合要求的。而进入下半区或上半区时,如果用[lo,mid]或[mid,hi],则mid位置被重复搜索。当区间只包含一个元素时,会导致死循环。

    上面这个基本的二分查找算法,未找到时,循环内需要做两次比较操作。可以稍作修改,使比较次数减少为一次。

int search1(int *vec, int N, int key)
{
	int lo = 0, hi = N - 1, mid;

	while (lo <= hi) {
		mid = lo + (hi - lo) / 2;
		if (key < vec[mid])
			hi = mid - 1;
		else
			lo = mid + 1;
	}

	if (lo > 0 && key == vec[lo - 1])
		return lo - 1;

	return -1;
}

    由于二分查找的效率已足够高(每次减半,区间缩小很快),就某次查找来说,第二种方法提升的效率不明显,而且没有第一种方法好理解,所以通常还是使用第一种方法。

    未找到时,lo,hi还有一个额外的作用(上面两种方法都适用),lo指向大于指定值的最近位置(可能超出上端);hi指向小于指定值的最近位置(可能超出下端)。

你可能感兴趣的:(二分查找,折半查找,search,算法)