二分查找(binary search)

1 二分查找的思想

每次将待查找元素与区间中间元素进行比较,将查找区间缩减为之前的一半,直到找到待查找元素或查找区间大小为0。

 

2 实现及关键点

2.1 关键点

1)循环退出条件

循环退出条件为low <= high,其中low为查找区间的下边界、high为上边界,而不是low < high。如果条件为low < high,那么当查找区间大小为1即low = high时,循环退出就无法与最后一个数据元素进行比较。

2)区间中间索引mid的取值

一般来说mid = (low + high) / 2,但是当low + high超出其数据类型范围时会造成溢出,所以这样的mid取值是不稳妥的。mid = low + (high - low) / 2这样减少了溢出的可能,或者mid = low + ((high - low) >> 1)这样通过位运算使得计算更加快速。

3)区间上下边界的更新

更新应为low = mid + 1、high = mid - 1而不是low = mid、high = mid。因为如果是low = mid、high = mid的更新方式,那么当查找区间为1时而最后的数据元素又不等于查找元素,那么程序会进入死循环。

2.2 实现

 1 /*查找升序数组中是否存在数据元素num,存在返回其在数组中的位置,不存在则返回-1*/
 2 int BinarySearch(int* pArrNum, int n, int num) {
 3     int low = 0;    //低索引
 4     int high = n - 1;    //高索引
 5     int mid;    //查找区间中间元素索引
 6 
 7     while (low <= high) {
 8         mid = low + ((high - low) >> 1);
 9         if (num == pArrNum[mid])    return mid;
10         else if (num > pArrNum[mid])    low = mid + 1;
11         else    high = mid - 1;
12     }
13 
14     return -1;
15 }

2.3 复杂度

时间复杂度O(logn),空间复杂度O(1)。

 

3 适用场景

1)有序数组。二分查找算法依赖于按照下标随机访问元素,所以必须是数组;其次,二分查找针对的数据必须是有序的,如果无序要先对其进行排序。

2)数据量太小不适合二分查找。如果数据量太小,例如只有10个数据元素,那么顺序查找就足够了。但是如果两个数据元素的比较操作耗时较多时,例如长度较大的字符串比较,那么还是二分查找合适,因为二分查找比较次数较少。

3)数据量太大不适合二分查找。数组这种数据结构的对内存依赖较大,要求分配连续的内存空间,当数据量较大时很可能会导致内存分配失败。

 

4 二分查找的变式

1)查找第一个值等于给定值的元素;

2)查找最后一个值等于给定值的元素;

3)查找第一个大于等于给定值的元素;

4)查找最后一个小于等于给定值的元素;

变体的二分查找问题要注意一下几个细节:循环退出条件、区间上下界更新方法和返回值选择。写二分查找代码时不要过于追求完美、整洁的写法,代码易读、没有bug更重要。

 1 /*查找第一个等于给定值的元素,存在返回其在数组中的位置,不存在则返回-1*/
 2 int SearchFirstElem(int* pArrNum, int n, int num) {
 3     int low = 0;
 4     int high = n - 1;
 5     int mid;
 6     int first = -1;    //第一个等于给定值的元素下表
 7 
 8     while (low <= high) {
 9         mid = low + ((high - low) >> 1);
10         if (pArrNum[mid] == num)    first = mid, high = mid - 1;
11         else if (pArrNum[mid] > num)    high = mid - 1;
12         else    low = mid + 1;
13     }
14 
15     return first;
16 }
17 
18 /*查找最后一个等于给定值的元素,存在返回其在数组中的位置,不存在则返回-1*/
19 int SearchLastElem(int* pArrNum, int n, int num) {
20     int low = 0;
21     int high = n - 1;
22     int mid;
23     int last = -1;    //最后一个等于给定值的元素下表
24 
25     while (low <= high) {
26         mid = low + ((high - low) >> 1);
27         if (pArrNum[mid] == num)    last = mid, low = mid + 1;
28         else if (pArrNum[mid] > num)    high = mid - 1;
29         else    low = mid + 1;
30     }
31 
32     return last;
33 }
34 
35 /*查找第一个大于等于给定值的元素,存在返回其在数组中的位置,不存在则返回-1*/
36 int SearchFirstGigOrEqualElem(int* pArrNum, int n, int num) {
37     int low = 0;
38     int high = n - 1;
39     int mid;
40     int first = -1;    //第一个大于等于给定值的元素下表
41 
42     while (low <= high) {
43         mid = low + ((high - low) >> 1);
44         if (pArrNum[mid] >= num)    first = mid, high = mid - 1;
45         else    low = mid + 1;
46     }
47 
48     return first;
49 }
50 
51 /*查找最后一个大于等于给定值的元素,存在返回其在数组中的位置,不存在则返回-1*/
52 int SearchLastLittleOrEqualElem(int* pArrNum, int n, int num) {
53     int low = 0;
54     int high = n - 1;
55     int mid;
56     int last = -1;    //第一个等于给定值的元素下表
57 
58     while (low <= high) {
59         mid = low + ((high - low) >> 1);
60         if (pArrNum[mid] <= num)    last = mid, low = mid + 1;
61         else    high = mid - 1;
62     }
63 
64     return last;
65 }

 

该篇博客是自己的学习博客,水平有限,如果有哪里理解不对的地方,希望大家可以指正!

posted @ 2019-05-07 15:03  zpchya  阅读(467)  评论(0编辑  收藏  举报