二分查找(binary search) 是一种基于分治策略的高效搜索算法。它利用数据的有序性,每轮缩小一半搜索范围,直至找到目标元素或搜索区间为空为止。一定不要忘了有序!
使用二分查找查找目标元素
问题描述
给定一个有序的整数数组 nums 和一个目标值 target,请编写一个函数来查找 target 在数组中的索引。如果目标值存在于数组中,则返回其索引;如果不存在,则返回 -1。
输入和输出
- 输入:一个有序的整数数组
nums和一个目标值target。 - 输出:
target的索引,如果不存在则返回-1。
示例
输入:nums = [10, 15, 20, 25, 30, 35, 40, 45, 50, 55], target = 25
输出:3
解释:目标值 25 在数组中的索引是 3。
输入:nums = [10, 15, 20, 25, 30, 35, 40, 45, 50, 55], target = 40
输出:6
解释:目标值 40 在数组中的索引是 6。
输入:nums = [10, 15, 20, 25, 30, 35, 40, 45, 50, 55], target = 100
输出:-1
解释:目标值 100 不存在于数组中,返回 -1。
题目分析
二分查找是一种在有序数组中查找目标元素的高效算法。与传统的线性查找不同,二分查找通过每次将查找范围减半,大大减少了查找的次数,使得它的时间复杂度为 O(log n)。
算法思想
-
初始化查找区间:将两个指针
i和j分别放在数组的起始和结束位置。 -
计算中间位置:计算中间元素的索引
m。 -
比较中间元素和目标值:
- 如果中间元素等于目标值,则查找成功,返回索引
m。 - 如果中间元素小于目标值,则移动左指针,将
i更新为m + 1。 - 如果中间元素大于目标值,则移动右指针,将
j更新为m - 1。
- 如果中间元素等于目标值,则查找成功,返回索引
-
重复上述步骤,直到查找区间为空或找到目标值为止。
二分查找的优势
二分查找的核心思想是“减半查找”,通过每次缩小一半的搜索区间,它的查找效率远高于线性查找。对于一个包含 n 个元素的有序数组,二分查找最多只需要进行 log2(n) 次比较即可找到目标元素。这使得二分查找在处理大规模数据时非常高效。
二分查找可视化
二分查找实现示例代码
public class Main {
// 定义一个内部静态类 binarysearch
public static class binarysearch {
public int binarySearch(int[] nums, int target) { // 确保方法为 public 以便可以访问
int i = 0;
int j = nums.length - 1;
while (i <= j) {
int m = i + (j - i) / 2;
if (nums[m] < target) {
i = m + 1;
} else if (nums[m] > target) {
j = m - 1;
} else {
return m;
}
}
return -1;
}
}
// main 方法
public static void main(String[] args) {
binarysearch search = new binarysearch();
// 测试用例 1: 数组中存在目标值 25
int[] nums1 = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
int target1 = 25;
int result1 = search.binarySearch(nums1, target1);
System.out.println("Test Case 1 - Target 25: " + (result1 == 3)); // 元素 25 的索引是 3
// 测试用例 2: 数组中存在目标值 40
int target2 = 40;
int result2 = search.binarySearch(nums1, target2);
System.out.println("Test Case 2 - Target 40: " + (result2 == 6)); // 元素 40 的索引是 6
// 测试用例 3: 数组中不存在目标值 100
int target3 = 100;
int result3 = search.binarySearch(nums1, target3);
System.out.println("Test Case 3 - Target 100: " + (result3 == -1)); // 返回 -1 表示未找到
}
}
一些需要注意的地方
值得注意的是,由于 i 和 j 都是
int类型,因此 i+j 可能会超出int类型的取值范围。为了避免大数越界,我们通常采用公式 来计算中点。(这样也可以,但是无法规避大树越界)
二分查找在时间和空间方面都有较好的性能。
- 二分查找的时间效率高。在大数据量下,对数阶的时间复杂度具有显著优势。
- 二分查找无需额外空间。相较于需要借助额外空间的搜索算法(例如哈希查找),二分查找更加节省空间。
然而,二分查找并非适用于所有情况,主要有以下原因。
- 二分查找仅适用于有序数据。若输入数据无序,为了使用二分查找而专门进行排序,得不偿失。因为排序算法的时间复杂度通常为 O(nlogn) ,比线性查找和二分查找都更高。对于频繁插入元素的场景,为保持数组有序性,需要将元素插入到特定位置,时间复杂度为 O(n) ,也是非常昂贵的。
- 二分查找仅适用于数组。二分查找需要跳跃式(非连续地)访问元素,而在链表中执行跳跃式访问的效率较低,因此不适合应用在链表或基于链表实现的数据结构。
- 小数据量下,线性查找性能更佳。在线性查找中,每轮只需 1 次判断操作;而在二分查找中,需要 1 次加法、1 次除法、1 ~ 3 次判断操作、1 次加法(减法),共 4 ~ 6 个单元操作;因此,当数据量 n 较小时,线性查找反而比二分查找更快。
总结
二分查找是解决有序数组中查找问题的高效算法,通过每次缩小一半的查找范围,可以显著提升查找效率。掌握二分查找不仅能帮助我们更好地理解查找算法的优化思想,还能为日后解决更复杂的问题打下坚实的基础。这块要好好掌握!