二分搜索算法测试数组不是从第一个元素开始,而是从中间的元素开始。如果该元素碰巧包含所需的值,则搜索结束。否则,中间元素的值要么大于要么小于正在搜索的值。如果它大于期望值,那么该值(如果它在列表中)将在数组的前半部分中找到;如果它小于期望值,那么该值(同样需要假设它在列表中)将在数组的后半部分中找到。在任何一种情况下,数组元素的一半就已经从进一步搜索范围中排除。
如果在中间元素中找不到所需的值,那么对于可能包含该值的一半数组,将重复该过程。例如,如果要搜索数组的后半部分,算法立即测试其中间元素。如果没有找到所需的值,那么搜索范围将缩小到该元素之前或之后的数组的四分之一。这个过程一直持续到被查找的值被找到,或者没有更多的元素要测试。
下面是一个函数的伪代码,它可以对以升序存储元素的数组执行二分搜索:
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; }
程序输出结果:
Enter the employee ID you wish to search for: 199
ID 199 was found in element 4 of the array.
二分搜索的效率
显然,二分搜索比线性搜索更有效率。每次进行比较,找不到所需的项目时,都会剔除必须搜索的数组剩余部分的一半。
例如,考虑有 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。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/22112.html