二分搜索(Binary Search)是一种比线性搜索更有效的巧妙算法。它唯一的要求是数组中的值是有序的。
二分搜索算法测试数组不是从第一个元素开始,而是从中间的元素开始。如果该元素碰巧包含所需的值,则搜索结束。否则,中间元素的值要么大于要么小于正在搜索的值。如果它大于期望值,那么该值(如果它在列表中)将在数组的前半部分中找到;如果它小于期望值,那么该值(同样需要假设它在列表中)将在数组的后半部分中找到。在任何一种情况下,数组元素的一半就已经从进一步搜索范围中排除。
如果在中间元素中找不到所需的值,那么对于可能包含该值的一半数组,将重复该过程。例如,如果要搜索数组的后半部分,算法立即测试其中间元素。如果没有找到所需的值,那么搜索范围将缩小到该元素之前或之后的数组的四分之一。这个过程一直持续到被查找的值被找到,或者没有更多的元素要测试。
下面是一个函数的伪代码,它可以对以升序存储元素的数组执行二分搜索:
Set first to 0 Set last to the last subscript in the array Set found to false Set position to -1 While found is not true and first is less than or equal to last Set middle to the subscript halfway between first and last If array[middle] equals the desired value Set found to true Set position to middle Else If array[middle] is greater than the desired value Set last to middle - 1 Else Set first to middle + 1 End If End While Return position
该算法使用 3 个索引变量:first、last 和 middle。first 和 last 变量标记当前正在搜索的数组部分的边界。它们用数组的第一个和最后一个元素的下标进行初始化。middle 则是在 first 和 last 元素之间大约中点位置的元素的下标,该值的计算方法就是釆用 first 和 last 相加求和然后除以 2,结果将被存储在 middle 变量中。
如果没有精确的中心元素,则使用整除法计算 middle 值,选择紧邻中点的元素。如果数组中间的元素不包含搜索值,则调整 first 或 last 变量,以便在下一次迭代期间只搜索数组的顶部或底部的一半。每次循环无法搜索到值时,都会将正在搜索的数组部分切割成一半。
以下 C++ 示例代码中的 binarySearch 函数将用于在整数数组上执行二分搜索。第一个形参 array,其元素的个数为 size,以下语句将搜索它是否出现了存储在 value 中的数字。如果找到该数字,则返回其数组下标。否则,返回 -1,表示该值没有出现在数组中。
int binarySearch(const int array[], int size, int value)
{
int first = 0, //第一个数组元素
last = size - 1, //最后一个数组元素
middle, //搜索的中点
position = -1; //搜索值的位置
bool found = false; // 标记
while (!found && first <= last)
{
middle = (first + last) / 2; // 计算中点
if (array[middle] == value) // 如果在中点发现值
{
found = true;
position = middle;
}
else if (array [middle] > value) // 如果值在下半部分
last = middle - 1;
else
first = middle + 1; //如果值在上半部分
}
return position;
}
下面的程序是一个使用 binarySearch 函数的完整程序。它将搜索一个包含员工 ID 号的数组,以查找特定的值。
//This program performs a binary search on an integer array whose elements are in ascending order.
#include <iostream>
using namespace std;
//Function prototype
int binarySearch(const int [], int, int);
const int SIZE = 20;
int main()
{
// Create an array of ID numbers sorted in ascending order
int IDnums[SIZE] = {101, 142, 147, 189, 199, 207, 222,234, 289, 296, 310, 319, 388, 394, 417, 429, 447, 521, 536, 600 };
int empID, // Holds the ID to search for
results;//Holds the search results
// Get an employee ID to search for
cout << "Enter the employee ID you wish to search for: ";
cin >> empID;
// Search for the ID
results = binarySearch(IDnums, SIZE, empID);
// If binarySearch returned -1, the ID was not found
if (results == -1)
cout << "That number does not exist in the array.\n";
else
{
// Otherwise results contains the subscript of
// the specified employee ID in the array
cout << "ID " << empID << " was found in element " << results << " of the array.\n";
}
return 0;
}
int binarySearch(const int array[], int size, int value)
{
int first = 0, // First array element
last = size - 1, // Last array element
middle, // Midpoint of search
position = -1; // Position of search value
bool found = false; // Flag
while (!found && first <= last)
{
middle = (first + last) / 2; // Calculate midpoint
if (array[middle] == value) // If value is found at mid
{
found = true;
position = middle;
}
else if (array[middle] > value) // If value is in lower half
last = middle - 1;
else
first = middle +1; // If value is in upper half
}
return position;
}
程序输出结果:
显然,二分搜索比线性搜索更有效率。每次进行比较,找不到所需的项目时,都会剔除必须搜索的数组剩余部分的一半。
例如,考虑有 20 000 个元素的数组。如果二分搜索未能在第一次尝试中找到某个项目,则剩余要搜索的元素数量为 10 000。如果在第二次尝试中未找到该项目,则仍然要搜索的元素的数量是 5000。这个过程一直持续到二分搜索找到所需的值或者确定它不在数组中。如果在数组中有 20 000 个元素,那么这最多需要 15 次比较。如果使用线性搜索的话,则平均需要进行 10 000 次比较,所以二分搜索的效率高太多了。
可以使用 2 的幂值来计算二分搜索在任何大小的数组上进行比较的最大次数。2 的幂值就是以 2 为底数的整数指数幂。只要找出大于数组中元素个数的 2 的最小幂值,即可知道查找一个元素所需的最大比较次数,或者确定它不存在。
例如,要在包含 50 000 个元素的数组中查找某个特定的项目,则最多只需要进行 16 次比较,因为 216 = 65 536;要在包含 1 000 000 个元素的数组中查找某个特定的项目,则最多只需要进行 20 次比较,因为 220= 1 048 576。