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 }
该篇博客是自己的学习博客,水平有限,如果有哪里理解不对的地方,希望大家可以指正!
来源:https://www.cnblogs.com/zpchya/p/10825509.html