代码随想录day1——binary search(二分查找)

161 阅读4分钟

在笔者看来二分查找(Binary Search)是一种在有序数组中高效查找元素的算法。它通过每次将搜索范围减半来快速缩小查找范围,从而减少搜索次数。二分查找的时间复杂度是 O(log n),相比于线性查找(O(n))更为高效。

v2-7fdf1463d6a48fd887714a3015ba5532_hd.jpg

二分查找的工作原理

  1. 初始条件:首先,你需要一个已经排序的数组。假设我们要在一个升序排列的数组中查找一个元素。

  2. 查找过程

    • 找到数组的中间元素(通过计算 mid = (low + high) / 2),然后将其与目标元素进行比较。
    • 如果中间元素等于目标元素,查找成功,返回该元素的位置。
    • 如果目标元素小于中间元素,则目标元素只可能在中间元素左侧,缩小查找范围,继续在左侧子数组中查找。
    • 如果目标元素大于中间元素,则目标元素只可能在中间元素右侧,缩小查找范围,继续在右侧子数组中查找。
    • 重复以上步骤,直到找到目标元素,或者查找范围为空(即左指针大于右指针,表示元素不存在)。

二分查找的关键点

  • 有序性:二分查找仅适用于有序数组(升序或降序)。
  • 时间复杂度:每次都将查找区间缩小一半,因此时间复杂度为 O(log n),其中 n 是数组的长度。
  • 空间复杂度:对于传统的二分查找算法,其空间复杂度是 O(1),因为它只使用常数空间来存储 lowhigh 和 mid 等变量。

二分查找的实现

下面是一个在升序排列的数组中进行二分查找的 Java 实现:

public class BinarySearch {

    // 二分查找方法,返回目标元素的索引,如果元素不存在则返回 -1
    public static int binarySearch(int[] arr, int target) {
        int low = 0;  // 查找的开始索引
        int high = arr.length - 1;  // 查找的结束索引

        // 当 low <= high 时,查找还没有结束
        while (low <= high) {
            // 计算中间索引
            int mid = low + (high - low) / 2;

            // 中间元素等于目标值,返回索引
            if (arr[mid] == target) {
                return mid;
            }
            // 如果目标值小于中间元素,继续在左半部分查找
            else if (arr[mid] > target) {
                high = mid - 1;
            }
            // 如果目标值大于中间元素,继续在右半部分查找
            else {
                low = mid + 1;
            }
        }

        // 如果没有找到目标元素,返回 -1
        return -1;
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};  // 已经排序的数组
        int target = 7;
        int result = binarySearch(arr, target);

        if (result == -1) {
            System.out.println("元素未找到");
        } else {
            System.out.println("元素 " + target + " 的索引是: " + result);
        }
    }
}

代码解析

  1. 参数说明

    • arr 是已经排序的数组。
    • target 是我们要查找的目标值。
  2. 查找过程

    • 使用 low 和 high 分别表示当前查找区间的左右边界。
    • mid 是当前区间的中间索引。
    • 每次比较中间元素与目标元素的大小,来决定接下来查找的区间是左半部分还是右半部分。
  3. 返回值

    • 如果找到目标值,返回目标值的索引。
    • 如果没有找到目标值,返回 -1

关于二分查找的变种

  1. 变种 1:查找左侧或右侧边界

    • 如果你需要查找一个元素的左边界(即第一个出现的位置)或右边界(即最后一个出现的位置),可以稍作修改,确保在找到目标元素时继续向左或向右搜索。
  2. 变种 2:处理重复元素

    • 如果数组中有重复元素,标准的二分查找会返回找到的任意一个位置。如果要找到特定的元素位置(如第一次出现的位置),需要对查找条件稍作调整。

二分查找的优化

  • 避免溢出:在计算 mid 时使用 low + (high - low) / 2,而不是 (low + high) / 2,这是为了防止当 lowhigh 的值过大时,直接相加可能导致溢出。

  • 递归实现:虽然上面的实现是迭代式的,二分查找还可以使用递归来实现:

    public static int binarySearchRecursive(int[] arr, int target, int low, int high) {
        if (low > high) {
            return -1;  // 元素未找到
        }
    
        int mid = low + (high - low) / 2;
    
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] > target) {
            return binarySearchRecursive(arr, target, low, mid - 1);
        } else {
            return binarySearchRecursive(arr, target, mid + 1, high);
        }
    }
    

总结

二分查找是一个高效的查找算法,它通过逐步缩小查找范围,使得查找过程的时间复杂度保持在 O(log n)。要确保应用二分查找,输入数据必须是有序的,否则该算法无法工作。