二分检索
二分检索(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 目录