【数据结构算法】简单的二分查找,大有文章

二分查找介绍

二分查找算法是一种用于在有序数组或列表中查找特定元素的方法。它通过反复将搜索空间一分为二,直到找到目标元素或确定其不存在。

二分查找算法

需求 :在有序数组 a 内,查找值 target

  • 如果找到返回索引
  • 如果找不到返回 -1
java 复制代码
public static int binarySearch(int[] a, int target) {
    int i = 0, j = a.length - 1;
    while (i <= j) {
        int m = (i + j) >>> 1;
        if (target < a[m]) {            // 在左边
            j = m - 1;
        } else if (a[m] < target) {        // 在右边
            i = m + 1;
        } else {
            return m;
        }
    }
    return -1;
}
  • binarySearch 方法接受一个数组 a 和目标值 target 作为参数。它初始化两个指针 i 和 j ,分别指向数组的起始和结束索引。
  • 算法进入一个 while 循环,在循环中,直到 i 指针大于 j 指针才会停止。在每次迭代中,它计算中间索引 m ,并将该索引处的元素与目标值进行比较。
  • 如果不加 i==j行不行? 不行,因为这意味着 i,j 指向的元素会漏过比较
  • 计算中间所以这里建议使用无符号右移>>>, 向右边移动一位,相当于除以2。如果使用(i + j)/2 ,i和j两个数很大相加,会超过int的范围,导致中间值变为负数。
  • 如果 m 处的元素等于目标值,方法返回索引 m ,表示找到了目标元素。如果 m 处的元素小于目标值,则将搜索范围缩小为数组的右半部分,更新 i 指针。相反,如果 m 处的元素大于目标值,则将搜索范围缩小为数组的左半部分,更新 j 指针。
  • 如果 while 循环完成而未找到目标元素,则方法返回 -1,表示在数组中未找到该元素。

二分查找时间复杂度

  • 黑色横线 O(1),常量时间,意味着算法时间并不随数据规模而变化
  • 绿色 O(log(n)),对数时间
  • 蓝色 O(n),线性时间,算法时间与数据规模成正比
  • 橙色 O(n*log(n)),拟线性时间
  • 红色 O(n^2) 平方时间
  • 黑色朝上 O(2^n) 指数时间

二分查找是一种高效的算法,其时间复杂度为 O(log n),其中 n 是数组中元素的个数。

时间复杂度

  • 最坏情况:O(log n)
  • 最好情况:如果待查找元素恰好在数组中央,只需要循环一次 O(1)

空间复杂度

  • 需要常数个指针 i,j,m,因此额外占用的空间是 O(1)

二分查找进阶

前面的需求都是找到一个满足条件的数据,现在需要返回相同元素最左侧的元素,比如[1, 4, 4, 5, 6],返回第一个4的索引位置,该如何实现呢?

java 复制代码
public static int binarySearchLeftmost1(int[] a, int target) {
    int i = 0, j = a.length - 1;
    int candidate = -1;
    while (i <= j) {
        int m = (i + j) >>> 1;
        if (target < a[m]) {
            j = m - 1;
        } else if (a[m] < target) {
            i = m + 1;
        } else {
            candidate = m; // 记录候选位置
            j = m - 1;     // 继续向左
        }
    }
    return candidate;
}

同样返回相同元素最右边的索引就不赘述了。

二分查找活学活用

题目:LC2594. 修车的最少时间

最近leetcode上推荐了一道题目,挺有意思的,大家可以思考下利用上面的知识如何解决。

思路

  • 如果时间为t, 根据题目能力值为 r 的机械工可以在 r * n2 分钟内修好 n 辆车,可得每个机械工修理的汽车数量是Math.sqrt(t/r)。
  • 假设最小时间10分钟,如果满足Math.sqrt(10/4) + Math.sqrt(10/2) +...+ Math.sqrt(10/1)>cars数量,意味着10分钟内修理10辆车绰绰有余,那么我尝试9分钟,知道找到最小的时间。
  • 这种存在单调性,我们可以使用二分法方法,左边从0或者1开始,右边从某一台花费的时间ranks[0]*cars*cars作为上界。

代码

java 复制代码
public long repairCars(int[] ranks, int cars) {
    // high必须要加1L,不然两个很大cars相乘int存不下
    long low = 0, high = 1L * ranks[0] * cars * cars;
    while (low <= high) {
        long mid = (low + high) >>> 1;
        long fixedCars = 0;
        for (int rank : ranks) {
            fixedCars += (long)Math.sqrt(mid / rank);
        }
        // 时间满足条件
        if(fixedCars >= cars) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low;
}
  • 注意这里上界限必须乘以1L,否则cars很大时int无法存储,出现负数的情况。
  • 时间复杂度:其中 n 为ranks 的长度,L 为二分的上界。
  • 空间复杂度:,过程中仅用到常数个变量。
相关推荐
怕浪猫11 小时前
GEO 优化到底是什么?AI时代内容创作者必须懂的新技能
算法·面试·github
朝朝辞暮i12 小时前
C++ 第 23 课:class —— 开始真正进入面向对象
开发语言·c++·算法
7yewh12 小时前
SLAM 三维空间刚体运动(2)
数据结构·人工智能·机器人·嵌入式·slam
Evand J13 小时前
【MATLAB例程】三维RRT+APF避障路径规划与到达角(AOA)定位算法|三维路径优化与定位仿真例程
算法·matlab·路径规划·代码·定位·rrt·aoa
leikooo13 小时前
用餐厅和盘子理解栈和堆
java·数据结构·链表·rust
weixin_3077791314 小时前
有限产能智能排产与动态重排智能体:从需求解构到技术实现
开发语言·人工智能·算法·架构
Ivanqhz14 小时前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
朝朝辞暮i14 小时前
C++ 第 27 课:智能指针 shared_ptr
开发语言·c++·算法
沙漠之主15 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
朝朝辞暮i15 小时前
C++ 第 29 课:回调函数 Callback
开发语言·c++·算法