二分检索

                                                                             二分检索

概述

    二分检索(Binary Search)也叫二分查找,是应用于有序表上的一种检索方法。二分检索的思想是:由于序列已经有序,故不需要顺序遍历,每次只需和序列中间位置的元素进行比较即可,以此确定下次查找的位置。显然每次都可以排除一半的元素,很高效。

伪代码

BinarySearch(Array, key)

1.low=0,high-n-1;

2.while(low<=high)

3.    mid=(low+high)/2;

4.    if(key<array[mid])

5.        high=mid-1;

6.    else if(key>array[mid])

7.        low=mid+1;

8.    else

9.        printf("查找成功!");

10. printf("查找失败!");

一个二分检索的例子

有序序列  2  5  7  9  12  15

下标      0  1  2  3  4   5

(i)查找 7

初始范围 low=0; high=5

第一次比较 范围[0,5]

mid=(low+high)/2=2;

array[2]=7

5<7,故 high=mid-1;

第二次比较 范围[0,1]

mid=(low+high)/2=0;

array[0]=2

5>2,故 low=mid+1;

第三次比较 范围[1,1]

mid=(low+high)/2=1;

array[1]=7

查找成功!

(ii)查找 6

初始范围 low=0; high=5

第一次比较 范围[0,5]

mid=(low+high)/2=2;

array[2]=7

6<7,故 high=mid-1;

第二次比较 范围[0,1]

mid=(low+high)/2=0;

array[0]=2

6>2,故 low=mid+1;

第三次比较 范围[1,1]

mid=(low+high)/2=1;

array[1]=7

6<7,故 high=mid-1;

此时 low=1, high=0; 不符合继续查询的条件,查找失败!

下面直接用完整的实例给出二分检索在两种查询区间下的代码:

代码

#include<stdio.h>
#include<stdlib.h>
/*
二分检索方法一
前提:序列已经从小到大排序
检索区间是[low,high]
*/
bool BinarySearch1(int array[], int n, int key)
{
	if (array && n > 0)
	{
		int low, high, mid;
		low = 0, high = n - 1;
		//查找区间[low,high],区间不同,high的变化也不同
		while (low <= high)
		{
			/*
			这种写法既高效,又可有效的避免溢出问题
			注意:右移运算符的优先级小于加法运算,所以需加括号
			*/
			mid = low + ((high - low) >> 1);
			if (key < array[mid])
				high = mid - 1;
			else if (key > array[mid])
				low = mid + 1;
			else//相等,则查找成功
				return true;
		}
	}
	//序列不存在或查找失败
	return false;
}
/*
二分检索方法二
前提:序列已经从小到大排序
检索区间是[low,high)
*/
bool BinarySearch2(int array[], int n, int key)
{
	if (array && n > 0)
	{
		int low, high, mid;
		//查找区间[low,high)
		low = 0, high = n;
		while (low < high)
		{
			mid = low + ((high - low) >> 1);
			if (key < array[mid])
				high = mid;
			else if (key > array[mid])
				low = mid + 1;
			else
				return true;
		}
	}
	return false;
}
//打印
void print(int array[], int n)
{
	if(array && n>0)
	{
		int i;
		for (i = 0; i < n; i++)
			printf("%4d",array[i]);
		printf("\n");
	}
}
int main()
{
	printf("***二分检索***by David***\n");
	int array[] = {2,5,7,9,12,15};
	int n = sizeof(array) / sizeof(array[0]);
	printf("原序列\n");
	print(array, n);
	int key;
	key = 7;
	printf("查找 %d\n",key);
	printf("方法一:");
	BinarySearch1(array, n, key) ? printf("查找成功!\n") : printf("查找失败!\n");
	printf("方法二:");
	BinarySearch2(array, n, key) ? printf("查找成功!\n") : printf("查找失败!\n");
	printf("\n");
	key = 6;
	printf("查找 %d\n", key);
	printf("方法一:");
	BinarySearch1(array, n, key) ? printf("查找成功!\n") : printf("查找失败!\n");
	printf("方法二:");
	BinarySearch2(array, n, key) ? printf("查找成功!\n") : printf("查找失败!\n");
	system("pause");
	return 0;
}

运行


   

有时需要返回查询的下标这时候代码可以这样写

/*二分检索
查找成功,返回下标
否则,返回-1
查询区间[low, high]
*/
int BinarySearch(int array[], int n, int key)
{
	if (array && n > 0)
	{
		int low, high, mid;
		//查找区间[low,high]
		low = 0, high = n - 1;
		while (low <= high)
		{
			mid = low + ((high - low) >> 1);
			if (key < array[mid])
				high = mid - 1;
			else if (key > array[mid])
				low = mid + 1;
			else//返回下标
				return mid;
		}
	}
	//查找失败
	return -1;
}
可以在while循环中插入一些代码打印low和high,观察low和high的变化,以加深对循环条件的认识。

优化

试想:如果待查找的元素在数组中多次出现,如何让返回的是第一次出现时的位置呢?

可以验证,以上的二分检索代码不能满足这种要求,它返回的可能是所有可能位置中的任何一个。

在《编程珠玑》中给出了满足这种要求的代码,如下:

int BSearch(int *arr, int n, int key)
{
	if (NULL == arr || n <= 0)
		return -1;
	int l, u, m, p;
	//find in (l, u)
	l = -1, u = n;
	while (l + 1 != u)
	{
		m = (l + u) / 2;
		//invariant: arr[l] < key <= arr[u]
		if (arr[m] < key) l = m;
		else u = m;
	}
	p = u;
	if (p >= n || arr[p] != key) p = -1;
	return p;
}
它假设 n >= 0 且 arr[-1] < key <= arr[n],当然,arr[-1]和arr[n]并不会被访问。如果key在数组中,则key第一次出现的位置就是u。



CCPP Blog 目录


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