关于二分查找(一)

185 阅读5分钟

屏幕截图 2024-10-27 182102.png 二分查找(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)

算法思想

  1. 初始化查找区间:将两个指针 ij 分别放在数组的起始和结束位置。

  2. 计算中间位置:计算中间元素的索引 m

  3. 比较中间元素和目标值

    • 如果中间元素等于目标值,则查找成功,返回索引 m
    • 如果中间元素小于目标值,则移动左指针,将 i 更新为 m + 1
    • 如果中间元素大于目标值,则移动右指针,将 j 更新为 m - 1
  4. 重复上述步骤,直到查找区间为空或找到目标值为止。

二分查找的优势

二分查找的核心思想是“减半查找”,通过每次缩小一半的搜索区间,它的查找效率远高于线性查找。对于一个包含 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 类型的取值范围。为了避免大数越界,我们通常采用公式 m=i+(ji)/2m=⌊i+(j−i)/2⌋ 来计算中点。(mid=(left+right)>>1mid=(left+right)>>1这样也可以,但是无法规避大树越界)

二分查找在时间和空间方面都有较好的性能。

  • 二分查找的时间效率高。在大数据量下,对数阶的时间复杂度具有显著优势。
  • 二分查找无需额外空间。相较于需要借助额外空间的搜索算法(例如哈希查找),二分查找更加节省空间。

然而,二分查找并非适用于所有情况,主要有以下原因。

  • 二分查找仅适用于有序数据。若输入数据无序,为了使用二分查找而专门进行排序,得不偿失。因为排序算法的时间复杂度通常为 O(nlog⁡n) ,比线性查找和二分查找都更高。对于频繁插入元素的场景,为保持数组有序性,需要将元素插入到特定位置,时间复杂度为 O(n) ,也是非常昂贵的。
  • 二分查找仅适用于数组。二分查找需要跳跃式(非连续地)访问元素,而在链表中执行跳跃式访问的效率较低,因此不适合应用在链表或基于链表实现的数据结构。
  • 小数据量下,线性查找性能更佳。在线性查找中,每轮只需 1 次判断操作;而在二分查找中,需要 1 次加法、1 次除法、1 ~ 3 次判断操作、1 次加法(减法),共 4 ~ 6 个单元操作;因此,当数据量 n 较小时,线性查找反而比二分查找更快。

总结

二分查找是解决有序数组中查找问题的高效算法,通过每次缩小一半的查找范围,可以显著提升查找效率。掌握二分查找不仅能帮助我们更好地理解查找算法的优化思想,还能为日后解决更复杂的问题打下坚实的基础。这块要好好掌握!

69272b9503e147cfede4cba0b7d0cfed.gif