



对于有序数组,可以采用更高效的二分查找策略。假设要在图1-10所示的数组中查找数字6。
图1-10
首先,将所需查找的数字6和数组正中间位置的元素5进行比较,如图1-11所示。
图1-11
由于6大于5,因此5及其左边的数字均不需要再考虑,可以将查找范围缩减一半,如图1-12所示。
图1-12
继续将所需查找的数字6和剩余元素中正中间位置的8进行比较,如图1-13所示。
图1-13
由于8大于6,因此8及其右边的数字均不需要再考虑,如图1-14所示。
图1-14
继续和剩余元素中正中间位置的6进行比较,发现6即为所查数字,查找结束,如图1-15所示。
图1-15
假设有序数组nums中存储了 n 个数字,变量key存储要查找的数字。二分查找算法比较key和数组nums正中间位置的元素值,如果key更大,则只需在数组nums右边一半的元素中查找;如果key更小,则只需在数组nums左边一半的元素中查找。重复执行上述逻辑,即可很快查找到目标。二分查找算法的伪代码如下。
1 low = 0 2 high = n - 1 3 while low <= high 4 mid = (low + high)/2 5 if key == nums[mid] 6 查找成功,跳出循环 7 if key < nums[mid] 8 high = mid - 1 9 if key > nums[mid] 10 low = mid + 1 11 if low > high 12 查找失败
二分查找算法的完整代码如1-3-1.cpp所示。
1-3-1.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <conio.h>
4 #include <time.h>
5
6 int main() // 主函数
7 {
8 srand((unsigned)time(NULL)); // 初始化随机种子
9 int nums[100]; // 数组存储多个数字
10 int i;
11
12 // 数组元素初始化为1到100
13 for (i = 0; i < 100; i++)
14 nums[i] = 1 + i;
15
16 printf("有序数组为:");
17 for (i = 0; i < 100; i++)
18 printf("%4d", nums[i]);
19 printf("\n");
20
21 // 生成要查找的数字,为1到100之间的随机整数
22 int key = 1 + rand() % 100;
23 int searchNum = 0; // 查找的次数
24 // 定义变量:查找区域的下边界、上边界、正中间
25 int low = 0, high = 100 - 1, mid;
26
27 // 以下进行二分查找
28 while (low <= high)
29 {
30 searchNum++; // 查找次数加1
31 mid = (low + high) / 2; // 正中间元素的序号
32 if (key == nums[mid]) // 找到了
33 {
34 printf("%d:查找到了,%d是要查找的数字\n", searchNum, nums[mid]);
35 break; // 跳出while循环语句
36 }
37 else
38 {
39 printf("%d:%d不是要查找的数字\n", searchNum, nums[mid]);
40 // 更新查找区域,变成上一步的一半
41 if (key < nums[mid]) // 下一次查找较小的一半数组
42 high = mid - 1;
43 else // 下一次查找较大的一半数组
44 low = mid + 1;
45 }
46 }
47 if (low > high) // 没有找到要查找的数字
48 printf("没有找到要查找的数字%d\n", key);
49 _getch();
50 return 0;
51 }
1-3-1.cpp的运行效果如图1-16所示。
图1-16
将1-2-2.cpp和1-3-1.cpp结合,即可实现对二分查找算法的动态过程的可视化,如代码1-3-2.cpp所示,运行效果参见图1-17,扫描右侧二维码观看视频效果“1.3 二分查找”。
1.3 二分查找
(a)第一次查找,未找到
(b)第二次查找,未找到
(c)第三次查找,未找到
(d)第四次查找,未找到
(e)第五次查找,未找到
(f)第六次查找,找到了
图1-17
1-3-2.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <time.h>
4 #include <conio.h>
5 #include <windows.h>
6
7 #define LEN 100
8
9 // 自定义函数,输出显示当前查找状态
10 // nums为要查找的数组,statuses记录数组元素的颜色,searchNUM为查找次数,
key为要查找的数值
11 void showArrays(int nums[], int statuses[], int searchNUM, int key)
12 {
13 int i;
14 Sleep(1000); // 暂停
15 system("cls"); // 清空画面
16 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7); // 设为白色
17 printf("在数组中查找:%d\n", key); // 输出白色提示文字
18
19 // 显示当前状态
20 for (i = 0; i < LEN; i++)
21 {
22 // 设为不同的颜色
23 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), statuses[i]);
24 printf("%4d", nums[i]); // 输出对应的数组元素
25 }
26 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7); // 设为白色
27 printf("\n查找次数:%d次\n", searchNUM); // 输出白色提示文字
28 }
29
30 int main() // 主函数
31 {
32 srand((unsigned)time(NULL)); // 初始化随机种子
33 int nums[LEN]; // 要查找的数组
34 int i;
35
36 // 数组元素初始化为1到100
37 for (i = 0; i < LEN; i++)
38 nums[i] = 1 + i;
39
40 // 根据查找状态设定颜色,未查找的数字显示为白色(7),已查找过的数字
显示为灰色(8),正在查找的数字显示为红色(4)
41 int statuses[LEN];
42 for (i = 0; i < LEN; i++)
43 statuses[i] = 7; // 初始全是未查找的数字,显示为白色(7)
44
45 // 生成要查找的数字,为1到100之间的随机整数
46 int key = 1 + rand() % LEN;
47 int id = 0; // 当前查找到的数组元素的索引
48
49 // 定义变量:查找区域的下边界、上边界、正中间
50 int low = 0, high = LEN - 1, mid = (low + high) / 2;
51 int searchNUM = 0; // 查找的次数
52
53 // 以下开始二分查找
54 while (low <= high)
55 {
56 mid = (low + high) / 2; // 正中间元素的序号
57
58 if (key != nums[mid]) // 当前元素不是目标
59 {
60 if (key < nums[mid]) // 下一次查找较小的一半数组
61 high = mid - 1;
62 else // 下一次查找较大的一半数组
63 low = mid + 1;
64 }
65
66 searchNUM++; // 查找次数加1
67 statuses[mid] = 4; // 设置正在查找的元素为红色
68 showArrays(nums, statuses, searchNUM, key); // 显示当前查找状态
69
70 for (i = 0; i < LEN; i++)
71 statuses[i] = 8; // 将数组中所有元素设为灰色
72 for (i = low; i <= high; i++)
73 statuses[i] = 7; // 将下一步要查找范围中的元素设为白色
74
75 if (key != nums[mid]) // 如果没有找到
76 statuses[mid] = 8; // 当前元素设为灰色,表示查找过了
77 else // 如果找到了
78 statuses[mid] = 4; // 当前元素设为红色,表示找到了
79 showArrays(nums, statuses, searchNUM, key); // 显示当前查找状态
80
81 if (key == nums[mid]) // 如果找到了
82 break; // 跳出循环
83 }
84 _getch();
85 return 0;
86 }