当前位置: 首页 > news >正文

二分查找算法精讲

二分查找算法精讲

  • 二分查找原理&闭区间写法
    • 34. 在排序数组中查找元素的第一个和最后一个位置
      • 关键的对与区间的定义
    • 代码
  • 左闭右开区间写法
    • 代码
  • 开区间写法(最推荐)
    • 代码
  • 如何处理不是>= 的情况

资料来源 二分查找红蓝染色法https://www.bilibili.com/video/BV1AP41137w7?vd_source=02eff3861fbae92e5089ad13f3b33f20&spm_id_from=333.788.videopod.sections

二分查找原理&闭区间写法

34. 在排序数组中查找元素的第一个和最后一个位置

34. 在排序数组中查找元素的第一个和最后一个位置

在这里插入图片描述

关键的对与区间的定义

关键:🔴牢牢记住这个区间的定义,是解题的关键!【补充】理解二分,请牢记区间的定义!区间内的数(下标)都是还未确定与 target 的大小关系的,有的是 < target,有的是 ≥ target;区间外的数(下标)都是确定与 target 的大小关系的。

在这里插入图片描述
在这里插入图片描述

代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int start = lowerBound(nums, target);
        if (start == nums.length || nums[start] != target) {
            return new int[]{-1, -1}; // nums 中没有 target
        }
        // 如果 start 存在,那么 end 必定存在
        int end = lowerBound(nums, target + 1) - 1;
        return new int[]{start, end};
    }

    // lowerBound 返回最小的满足 nums[i] >= target 的下标 i
    // 如果数组为空,或者所有数都 < target,则返回 nums.length
    // 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]
    private int lowerBound(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1; // 闭区间 [left, right]
        while (left <= right) { // 区间不为空
            // 循环不变量:
            // nums[left-1] < target
            // nums[right+1] >= target
            int mid = left + (right - left) / 2;
            if (nums[mid] >= target) {
                right = mid - 1; // 范围缩小到 [left, mid-1]
            } else {
                left = mid + 1; // 范围缩小到 [mid+1, right]
            }
        }
        // 循环结束后 left = right+1
        // 此时 nums[left-1] < target 而 nums[left] = nums[right+1] >= target
        // 所以 left 就是第一个 >= target 的元素下标
        return left;
    }
}

左闭右开区间写法

代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int start = lowerBound(nums, target);
        if (start == nums.length || nums[start] != target) {
            return new int[]{-1, -1}; // nums 中没有 target
        }
        // 如果 start 存在,那么 end 必定存在
        int end = lowerBound(nums, target + 1) - 1;
        return new int[]{start, end};
    }

    // lowerBound 返回最小的满足 nums[i] >= target 的下标 i
    // 如果数组为空,或者所有数都 < target,则返回 nums.length
    // 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]
    private int lowerBound(int[] nums, int target) {
        int left = 0;
        int right = nums.length; // 左闭右开区间 [left, right)
        while (left < right) { // 区间不为空
            // 循环不变量:
            // nums[left-1] < target
            // nums[right] >= target
            int mid = left + (right - left) / 2;
            if (nums[mid] >= target) {
                right = mid; // 范围缩小到 [left, mid)
            } else {
                left = mid + 1; // 范围缩小到 [mid+1, right)
            }
        }
        // 循环结束后 left = right
        // 此时 nums[left-1] < target 而 nums[left] = nums[right] >= target
        // 所以 left 就是第一个 >= target 的元素下标
        return left;
    }
}

开区间写法(最推荐)

代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int start = lowerBound(nums, target);
        if (start == nums.length || nums[start] != target) {
            return new int[]{-1, -1}; // nums 中没有 target
        }
        // 如果 start 存在,那么 end 必定存在
        int end = lowerBound(nums, target + 1) - 1;
        return new int[]{start, end};
    }

    // lowerBound 返回最小的满足 nums[i] >= target 的下标 i
    // 如果数组为空,或者所有数都 < target,则返回 nums.length
    // 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]
    private int lowerBound(int[] nums, int target) {
        int left = -1;
        int right = nums.length; // 开区间 (left, right)
        while (left + 1 < right) { // 区间不为空
            // 循环不变量:
            // nums[left] < target
            // nums[right] >= target
            int mid = left + (right - left) / 2;
            if (nums[mid] >= target) {
                right = mid; // 范围缩小到 (left, mid)
            } else {
                left = mid; // 范围缩小到 (mid, right)
            }
        }
        // 循环结束后 left+1 = right
        // 此时 nums[left] < target 而 nums[right] >= target
        // 所以 right 就是第一个 >= target 的元素下标
        return right;
    }
}

如何处理不是>= 的情况

在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/106589.html

相关文章:

  • Mysql 的binlog日志的优缺点
  • 【多通道数据采集系统:汽车测试江湖的“兵器谱”——硬核分类与实战秘籍】
  • 大模型(LLMs)RAG 版面分析——表格识别方法篇
  • 二分类交叉熵以及加权交叉熵
  • 批量删除或替换文本文件中指定的行,如删除第一行、删除最后一行
  • 诠视科技Unity SDK开发环境配置、项目设置、apk打包。
  • 利用空间-运动-回波稀疏性进行5D图像重建,以实现自由呼吸状态下肝脏定量磁共振成像(MRI)的加速采集|文献速递--深度学习医疗AI最新文献
  • 穿透单链表的神秘屏障,洞察数据结构的真谛
  • 瑞萨RA-Eco-RA4M2-100PIN-V1.0MCU使用心得
  • 超级好用的小软件,连接电脑和手机。
  • AI 大模型应用开发实战营-毕业总结
  • SQLSugar单列查询Select和条件查询Where的封装
  • 整点报时时间HTML源码
  • gitee 配置git上传
  • 扫描线离散化线段树解决矩形面积并-洛谷P5490
  • 《电流与电压的誓约》
  • (六)窗口表面
  • 【SPP】蓝牙串口配置中LM互操作性要求深度解析
  • 解决elementui-plus使用el-table的合计功能时横向滚动条显示在了合计上方
  • 基于SpringBoot的河道水情大数据可视化分析平台设计与实现(源码+论文+部署讲解等)
  • 在 Rocky Linux 9.2 上编译安装 Redis 6.2.6
  • MaxScript 实现冒泡排序算法
  • .Net8项目使用docker、docker-compose部署步骤
  • C++STL——容器-vector(含部分模拟实现,即地层实现原理)(含迭代器失效问题)
  • Spark大数据分析与实战笔记(第四章 Spark SQL结构化数据文件处理-03)
  • uni-app:firstUI框架的选择器Select改造,添加一个搜索的插槽
  • 以 dockurr/windows 容器运行一个windows 操作系统
  • QML输入控件: Slider的高级外观定制(音视频控制条)
  • 接口测试及常用接口测试工具
  • 【C语言】深入理解指针(五):sizeof、strlen与数组指针的那些事儿