1.定义
二分查找(也称为二分搜索)是一种在有序数组中查找特定元素的高效算法。这种算法的效率比线性搜索要高得多,因为它每次比较后都会将搜索范围缩小一半。
2.工作原理
(1)初始设置:一个已排序的数组和一个目标值。然后,设置两个指针,通常称为 low
和 high
,分别指向数组的起始位置和末尾,找到中间元素mid。
(2)比较中间元素:
·如果 mid
位置的元素等于目标值,则搜索成功,返回 mid
的位置。
·如果 mid
位置的元素小于目标值,则目标值应该在 mid
的右侧。因此,将 low
设置为 mid + 1
,并在新范围内重复搜索。
·如果 mid
位置的元素大于目标值,则目标值应该在 mid
的左侧。因此,将 high
设置为 mid - 1
,并在新范围内重复搜索。
(3)重复步骤:重复上述步骤,直到 low
大于 high
,这表明目标值不在数组中,此时应返回一个错误指示(如 -1)。
3.二分法查找的时间复杂度分析
(1)初始搜索区间:开始时,搜索区间是整个数组,设数组长度为 n。
(2)减少搜索区间:每进行一次比较,搜索区间减少到原来的一半。所以,在第一次比较后,区间大小变为 n/2,第二次为 n/4,第三次为 n/8,依此类推,可以发现这是一个等比数列。
(3)确定操作次数:要找出这个过程需要重复多少次才能将搜索区间减少到只包含一个元素(或没有元素),我们可以设置等式 =1,其中k是所需的步骤数。解这个等式得到 k=。
(4)时间复杂度:
①每一步的时间复杂度:不论数组有多大,计算中间点和比较两个数的时间都是固定的。所以每执行一次比较操作(包括计算中点和比较元素),其时间复杂度是常量,记为 C。
②步骤数: k=
③总时间复杂度:算法的总时间复杂度 = 步骤数乘以每一步的时间复杂度,即C×k。将 k 替换为 llog2n,得到总时间复杂度C×log2n。
④简化表示:省略常量和低阶项,最终的时间复杂度表示为O(logn)。
二分查找非常高效,其时间复杂度为 O(log n),远优于线性搜索的 O(n)。
4.二分法查找代码实现
public class BinarySearch {
public static int binarySearch(int[] arr, int target) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2; // 防止溢出的写法
if (arr[mid] == target) {
return mid; // 找到目标值
} else if (arr[mid] < target) {
low = mid + 1; // 目标在右侧
} else {
high = mid - 1; // 目标在左侧
}
}
return -1; // 没找到目标值
}
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9, 11};
int target = 7;
int result = binarySearch(arr, target);
if (result == -1) {
System.out.println("Element not found");
} else {
System.out.println("Element found at index: " + result);
}
}
}
【注意】
1.循环退出条件是low<=high,而不是 low<high。下面是一个简单的例子:
数组 [1, 2, 3, 4, 5]
,目标值为 5。使用二分查找:
- 初始时,
low = 0
,high = 4
。
- 第一次循环后,
mid = 2
,arr[mid] = 3
,小于 5,因此low = mid + 1 = 3
。
- 第二次循环,
low = 3
,high = 4
,mid = 3
,arr[mid] = 4
,仍小于 5,因此low = mid + 1 = 4
。
- 第三次循环,
low = 4
,high = 4
,此时low
和high
相等,mid = 4
,arr[mid] = 5
,找到目标值。
如果使用 low < high
作为条件,第三次循环就不会发生,从而错过了目标值。
2.在二分查找算法中,如果 low
和 high
都是较大的整数,那么 (low + high) / 2
的计算可能会导致整数溢出。
避免溢出的中点计算方法:
int mid = low + (high - low) / 2;
它实际上等同于 (2*low + high - low) / 2,
这种方法仍然正确地计算了中点。
5.二分法的优点和局限性
(1)优点:二分查找非常高效,其时间复杂度为 O(log n),远优于线性搜索的 O(n)。
(2)局限性:
①二分查找只适用于已排序的数组。对于其他数据结构是不可以的。
②在实际应用中,数据集可能是动态的,即需要频繁地插入或删除元素。每次在这样的数据集中插入或删除元素后,为了继续使用二分查找,我们必须保持数据集的有序状态。插入或删除操作可能需要移动大量元素来填补空位或为新元素腾出空间。这种操作的高成本可能会抵消二分查找带来的效率优势。因此,二分法查找它最适用于那些不经常变动的有序数据集。
③二分法查找适用于中到大规模的有序数据集。数据量太小或太大都可能不适宜使用二分查找。
·对于非常小的数据集,二分查找的设置和维护可能不值得。如果数据项只有几个,简单的线性搜索可能更高效,因为线性搜索不需要数据预先排序,也没有额外的设置开销。
·于非常大的数据集,二分查找可能受限于内存容量。因为二分查找通常是在数组上进行的,而数组需要在内存中连续存储。如果数据集太大以至于无法完整地加载到内存中,那么二分查找就无法直接使用。
有用请点个赞哦!