二分法查找
- 问题引入
- 在有序数列中查找
问题引入
问:有15个犯罪嫌疑人排成一排,其中只有1个是真正的“犯人”。你要通过问他们“犯人在哪里?”来找出真正的犯人。没问一次问题都会得到以下3种答案:
- 我是犯人
- 犯人在我左边
- 犯人在我右边
如何仅通过3次文化,就找到真正的犯人?
解:每次都从中间提问,然后根据回答剩下一半的人,接着再从剩下的一半的中间开始问。这样第1次问完后,就可以筛选出7人,第2次提问后,筛选出3人,第3次提问后,就可以锁定1人了。
将该问题推广,“通过
n
n
n次提问,最多能从多少人中锁定犯人”,假设嫌疑人总人数为
P
(
n
)
P(n)
P(n):
P
(
n
)
=
{
1
,
(
n
=
0
)
P
(
n
−
1
)
+
1
+
P
(
n
−
1
)
,
(
n
=
1
,
2
,
3
,
⋯
)
P(n)=\begin{cases} 1, & (n=0) \\ P(n-1)+1+P(n-1), & (n=1,2,3,\cdots) \end{cases}
P(n)={1,P(n−1)+1+P(n−1),(n=0)(n=1,2,3,⋯)
或者
P
(
n
)
=
2
n
+
1
−
1
P(n)=2^{n+1}-1
P(n)=2n+1−1
这种从有序数据中找出目标数据时总是判断秒数据所在范围内正中间数据的方法,叫作“二分法”或“二分查找”。
它能以指数的速度减低查找的次数。
在有序数列中查找
现在将情景转变到在计算机中查找数据:
问:在下面从小到大排列的15个数中,查找出67的位置。
16 , 17 , 23 , 29 , 31 , 42 , 45 , 58 , 62 , 66 , 67 , 71 , 78 , 83 , 88 16,17,23,29,31,42,45,58,62,66,67,71,78,83,88 16,17,23,29,31,42,45,58,62,66,67,71,78,83,88
二分法查找的代码(C++ 20)如下:
import <iostream>;
import <vector>;
import <format>;
using namespace std;
int main()
{
vector<int> arr{ 16, 17, 23, 29, 31, 42, 45, 58, 62, 66, 67, 71, 78, 83, 88 };
int size{ static_cast<int>(arr.size()) / 2 };
const int obj{ 67 };
while (arr.at(size) != obj) {//如果中间不是
if (arr.at(size) > obj) {//如果在左边
size = size / 2;
}
else if (arr.at(size) < obj) {//如果在右边
size = (15 + size) / 2;
}
}
cout << "=====二分法查找=====" << endl;
cout << "在以下数组中:" << endl;
for (auto arr : arr) {
cout << arr << "; ";
}
cout << endl;
cout << format("{}位于数组的第{}位", obj, size+1) << endl;
}
输出结果如下: