【算法】使用二分查找解决算法问题:理解二分法思想,模板讲解与例题实践

文章目录

  • 二分算法思想 / 性质 / 朴素模板
    • 二分查找的引入(二段性)
      • 704.二分查找
    • 模板
      • 34.在排序数组中查找元素的第一个和最后一个位置
    • 二分查找的前提条件 / 时间复杂度分析
  • 算法题
    • 69.x的平方根
    • 35.搜索插入位置
    • 852.山脉数组的峰顶索引
    • 162.寻找峰值
    • 153.寻找旋转排序数组中的最小值
    • LCR173.点名

二分算法思想 / 性质 / 朴素模板

二分查找的引入(二段性)

  1. 首先,关于二分的题,重点在于理解二分法思想,当理解后,模板自然便可写出来,变成简化思路的工具。
  2. 我们通过下面的一道题,理解二分查找算法思想(并简单了解所谓模板)

704.二分查找

在这里插入图片描述

对于该题,我们首先思考 暴力解法

  1. 很简单,遍历数组,如果找到该数则返回下标,否则返回-1
  2. 我们知道:当数组元素很多时,或每次目标值与起始位置很远时,暴力解法的时间开销是很大的

此时我们引入一个思考:

  1. 当我们在数组中随机选取一个数x:
    • 如果x<target,那么目标值一定在x后面(x前面的数就不用再看了);
    • 当x>target,我们就可以直接去前面找目标值。
  2. 此时数组就被分成了两部分,即x前面的部分和x后面的部分,我们跟随查找条件直接去到相应的区间即可。
  3. 我们可以感知到题目由x与target的关系被分成了两段,这就是二段性
  4. 当一个题目有二段性的时候,我们可以采用二分法解题。

在这里插入图片描述

代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1; // 左右区间边界
        while(left <= right)
        {
            int mid = left + (right - left + 1) / 2; // 更新 mid
            if(nums[mid] < target) // 当前值<目标值 : 更新左边界
                left = mid + 1;    
            else if(nums[mid] > target) // 当前值>目标值 : 更新右边界
                right = mid - 1;
            else // 找到目标值,返回下标
                return mid;
        }
        return -1; // 未找到,返回-1
    }
};

上面的代码很好理解:

  • 当左指针未超过右指针时,每次更新中间指针mid,通过nums[mid] 与 target的关系比,更新左右边界,直到找到target

模板

当我们理解了二分思想,对于一道题,重点在于分析出该题具有二段性,随后写代码就不是难事了,但这里还是简单写出朴素模板:

while(left <= right)
{
    int mid = left + (right - left + 1) / 2;
    if(...) // 根据题意左右边界的更新也有所不同
        left = mid + 1;
        // left = mid;
    else if(...)
        right = mid - 1;
        // right = mid;
    else
    	return ...;
}

这里需要注意的是mid 的更新:

  1. 平时我们有mid = (left + right) / 2写法来进行中间值的更新,这里不提倡这种写法。因为当right和left过大,这种写法会造成整形溢出

  2. 而对于mid = left + (right - left + 1) / 2mid = left + (right - left) / 2是否+1,我们分为下面的情况
    在这里插入图片描述

  3. 那么我们什么时候采用法①或采用法②

    • 当代码下面出现“-1”的时候,我们更新mid时就“+1”
    • 即当我们有了后续更新left、right边界后,通过判断是否有mid - 1,在求中间值时是否将(right - left + 1)部分改为(right - left + 1)
  4. 我们通过下面的一道题来理清二分查找的细节处理。


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

在这里插入图片描述

思路

  1. 题目给出了一个非递减顺序的数组(即要么递增要么重复),要求O(logn)的时间复杂度,这两点其实就可以想到要用二分了(
  2. 根据题目要求,我们需要找到目标值的左右端点:
    • 找左端点,数组被分成两部分:① 小于t ② 大于等于t
    • 找右端点,数组分为两部分:① 小于等于t ② 大于t
    • 这样分组可以让我们通过更新区域来找到端点
  3. 此时题目有明显的二段性,我们可以使用二分查找来解题
  4. 由于此题为了解释二分代码的细节问题,会花篇幅进行细节解释:

细节问题

  1. 首先关于循环条件:使用left < right 而不是 <=。
    在这里插入图片描述

  2. 关于求中点的操作:
    我们直接选取一个极端情况:当区间只剩下两个元素时。

    • 对于找左端点的情况
      在这里插入图片描述
      由上图我们知道,当找左端点时:应该使用①进行求中点操作。
  3. 左右区间的更新

    • 对于求左端点的情况:
      在这里插入图片描述
    • 对于求右端点的情况:
      在这里插入图片描述

代码

vector<int> searchRange(vector<int>& nums, int target) {
    // 处理边界情况
    if(nums.size() == 0)    return {-1, -1};

    // 二分查找
    int left = 0, right = nums.size() - 1;
    // 1. 找左区间端点
    while(left < right)
    {
        int mid = left + (right - left) / 2;
        if(nums[mid] < target)  left = mid + 1;
        else right = mid;
    } // left与right相遇找到左区间端点
    int begin = 0;
    if(nums[left] != target) return {-1, -1};
    else begin = left;

    // 2. 找右区间端点
    right = nums.size() - 1;
    while(left < right)
    {
        int mid = left + (right - left + 1) / 2;
        if(nums[mid] <= target) left = mid;
        else right = mid - 1;
    }
    // int end = right;
    return {begin, right};
}

二分查找的前提条件 / 时间复杂度分析

使用条件

  1. 二分查找算法必须在有序的序列中才能使用。

  2. 二分查找的核心思想是通过比较中间位置的元素与目标元素的大小关系,确定目标元素可能存在的区域。如果数组是无序的,那么就无法保证中间位置的元素与目标元素的大小关系。

时间复杂度

二分查找的每一次迭代中,会将查找区域划分为两个子区域,并通过比较中间位置的元素与目标元素的大小关系,确定目标元素可能存在的区域。这样,每一次迭代都能将查找区域缩小一半。

假设要查找的数组长度为n,每次迭代后查找区域的长度会减少一半,直到找到目标元素或者确定目标元素不存在。因此,最坏情况下,二分查找的迭代次数为 k,满足 n / 2^k = 1。

通过求解上述方程可以得到 k = log2(n),即二分查找的时间复杂度为 O(log n)。


算法题

69.x的平方根

在这里插入图片描述

  1. 如图将数组分为 小于等于x大于x 两段区间
  2. 根据二分法:
    • mid 的平方小于等于 x,则更新 left 为 mid
    • mid 的平方大于 x,则更新 right 为 mid - 1

思路

在这里插入图片描述

代码

int mySqrt(int x) {
    // 处理边界情况
    if(x < 1) return 0;

    long long left = 0, right = x;
    // 二分法
    while(left < right)
    {
        long long mid = left + (right - left + 1) / 2;
        if(mid * mid <= x) left = mid;
        else right = mid - 1; // 出现mid-1,上面求mid用"+1"
    }

    return (int)left;
}

35.搜索插入位置

在这里插入图片描述

思路

  1. 根据题目:排序数组、返回索引,O(logn)的算法,此时大概率可以用二分解题
    在这里插入图片描述

代码

  1. 写代码时注意几点:根据上文介绍的,由我们更新left,right的方式,while循环中写left < right
  2. 当循环结束后,有两种可能:
    • 找不到target:则target应该插入到数组外,即下标为left+1
    • 找到了:left即为待插入位置
int searchInsert(vector<int>& nums, int target) {
    // 二分法
    int left = 0, right = nums.size() - 1;
    while(left < right)
    {
        int mid = left + (right - left) / 2;
        if(nums[mid] < target) left = mid + 1;
        else right = mid;
    }
    if(nums[left] < target) return left+1; // 数组中无target,插入位置在数组外
    else return left; // 数组中有t / 无t+插入位置在数组内
}

852.山脉数组的峰顶索引

在这里插入图片描述
思路

  1. 题目看起来比较模糊,通过看示例基本可以理解,简单理解就是:数组存在一个峰值,峰值左边的数是递增的,峰值右边的数是递减的。
    在这里插入图片描述
    代码
int peakIndexInMountainArray(vector<int>& arr) {
    int left = 0, right = arr.size() - 1;
    // 峰值左侧区间 arr[i] > arr[i-1] 向右找 left = mid;
    // 峰值右侧区间 arr[i] < arr[i-1] 向左找 right = mid - 1;
    while(left < right)
    {
        int mid = left + (right - left + 1) /2;
        if(arr[mid] > arr[mid-1]) left = mid;
        else right = mid - 1;
    }
    return left; // 返回峰值索引
}

162.寻找峰值

在这里插入图片描述

思路

在这里插入图片描述

代码

int findPeakElement(vector<int>& nums) {
    int left = 0, right = nums.size() - 1;
    // 取下标i,如果有num[i] > nums[i+1] 这两个数是递减,则这两数左侧必定存在一个峰值
    // 如果nums[i] < nums[i] 则右侧一定存在峰值
    while(left < right)
    {
        int mid = left + (right - left) / 2;
        if(nums[mid] > nums[mid + 1])   right = mid;// 找左区间
        else left = mid + 1;
    }
    return left;
}

153.寻找旋转排序数组中的最小值

在这里插入图片描述

思路
在这里插入图片描述

代码

int findMin(vector<int>& nums) {
    int n = nums.size();
    int left = 0, right = n - 1;
    // 最小值的左侧区间,nums[i] < nums[n-1] 成立
    // 右侧区间,nums[i] > nuns[n-1] 成立
    // 二段性->二分法
    while(left < right)
    {
        int mid = left + (right - left) / 2;
        if(nums[mid] > nums[n-1])  left = mid + 1;
        else right = mid; 
    }  
    return nums[left];
}

LCR173.点名

在这里插入图片描述
思路

  1. 实际就是找0~n-1中缺失的一个数字
  2. 由于数组中少了一个数字,我们可以知道:
    • 在缺少的数字x左侧,满足:值==下标
    • 缺少的数字x右侧,满足:值>下标(即值==下标+1)
    • 据此得到二段性,可以使用二分法
  3. 值得一提的事,这道题解法很多,哈希、位运算、直接遍历、数学方式等,都可以尝试。

代码

int takeAttendance(vector<int>& records) {
    // 缺失的数左区间满足:值=下标
    // 缺失的数右区间满足:值>下标
    // 二段性->二分法
    int n = records.size();
    int left = 0, right = n - 1;

    while(left < right)
    {
        int mid = left + (right - left) / 2;
        if(records[mid] == mid) left = mid + 1;
        else right = mid;
    }
    // 处理边界情况:当确实的数为n的时候
    if(records[right] == right) return n;
    return right;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/263879.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

服务熔断(Hystrix)

服务雪崩 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其他的微服务&#xff0c;这就是所谓的“扇出”&#xff0c;如果扇出的链路上某个微服务的调用响应时间过长&#xff0c;或者不可用&#xff0c;对微服务A的…

《每天一分钟学习C语言·六》

1、 1字节&#xff08;Byte&#xff09;8位&#xff0c;1KB1024字节&#xff0c;1M1024KB&#xff0c;1G1024MB 2、 char ch A; printf(“ch %d\n”, ch);ch为65 这里是ASCII码转换 3、 scanf("%d", &i); //一般scanf直接加输入控制符 scanf("m%d&qu…

TKEStack容器管理平台实战之部署wordpress应用

TKEStack容器管理平台实战之部署wordpress应用 一、TKEStack介绍1.1 TKEStack简介1.2 TKEStack特点1.3 TKEStack架构图 二、kubernetes集群介绍2.1 k8s简介2.2 k8s架构图 三、本次实践介绍3.1 实践环境要求3.2 本次实践环境规划3.3 本次实践简介 四、安装容器管理平台4.1 安装T…

WinRAR如何设置和清除密码?

WinRAR是一款功能强大的压缩管理器&#xff0c;除了能把文件打包变小&#xff0c;还能给压缩包设置密码保护&#xff0c;让文件不能随意打开&#xff0c;不需要时还可以把密码取消。下面来说说具体怎么操作吧。 WinRAR根据需要可以设置单次密码和永久密码&#xff0c;我们分别…

C++ Qt开发:Charts绘图组件概述

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍QCharts二维绘图组件的常用方法及灵活运用。 …

Git 配置多个 SSH-Key

Git 配置多个 SSH-Key &#xff08;两个都是gitee&#xff09; 先看图&#xff0c;官网固然重要&#xff0c;但是不完全行&#xff08;因为官网示例是一个gitee一个github&#xff09;&#xff0c;现在想是想多个都是gitee在他上面稍微更改即可 一般不对遇到这种问题&#xf…

Windows电脑向ipad和iOS系统共享文件夹

Windows电脑向ipad和iOS系统共享文件夹 这个方案不需要下载任何软件&#xff0c;但是要求 iOS 和 Windows 在同一个局域网内。再大的文件都可以在 iOS13 自带的的“文件App”里实时显示&#xff0c;可以直接打开。这个解决方案需要你 Windows 电脑上登陆了微软账号&#xff0c…

数字技术:引领未来的创新驱动力

数字技术&#xff0c;作为当代最具创新性和影响力的技术领域之一&#xff0c;已经在全球范围内引起了广泛的关注和研究。当前&#xff0c;数字技术正以惊人的速度改变着我们的世界&#xff0c;从日常生活到商业领域&#xff0c;无一不受到其影响。数字技术的发展不仅改变了人们…

深度学习美化图片,绝对可行,美化效果挺好 DPED

一、背景 要美化生成的图片的效果&#xff0c;找到一个 效果如下&#xff1a; 二、步骤 1、python3.6环境&#xff0c;TensorFlow 2.0.0 2、下载代码&#xff1a;https://github.com/aiff22/DPEDx 3、将要增强的照片放在以下目录中&#xff0c;没有就新建&#xff1a; dpe…

基于改进YOLOv7的绝缘子缺陷检测算法

摘要 现有的检测方法面临着巨大的挑战&#xff0c;在识别绝缘子的微小缺陷时&#xff0c;针对输电线路图像与复杂的背景。为保证输电线路的安全运行&#xff0c;提出一种改进的YOLOv 7模型&#xff0c;以提高检测结果。 首先&#xff0c;基于K-means对绝缘子数据集的目标盒进…

Python并行编程详解:发挥多核优势的艺术

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在当今计算机时代&#xff0c;充分发挥多核处理器的性能是提高程序运行效率的关键。Python作为一门强大的编程语言&#xff0c;提供了多种并行编程工具和库。本文将深入介绍Python中的并行编程&#xff0c;探讨如…

Python命名规范中的[单/双][前导/后缀]下划线小结

如图所示 出处 Single and Double Underscores in Python Names

泽攸科技SEM台式扫描电子显微镜

泽攸科技是一家国产的科学仪器公司&#xff0c;专注于研发、生产和销售原位电镜解决方案、扫描电镜整机、台阶仪、探针台等仪器。目前台式扫描电镜分为三个系列&#xff1a;ZEM15、ZEM18、ZEM20。 ZEM15台式扫描电镜&#xff1a; ZEM18台式扫描电镜&#xff1a; ZEM20台式扫描…

Rust报错:the msvc targets depend on the msvc linker but `link.exe` was not found

当我在我的 windows 电脑上安装 rust&#xff0c;然后用 cargo 新建了一个项目后&#xff0c;cargo run 会报错&#xff1a; error: linker link.exe not found| note: program not foundnote: the msvc targets depend on the msvc linker but link.exe was not foundnote: p…

H5页面这样测试,让Bug无处可逃!

部门最近的H5相关项目挺多的&#xff0c;由于团队之前接触的大多是Web项目&#xff0c;很少涉及H5&#xff0c;想着给团队成员培训下&#xff0c;减少漏测率&#xff0c;于是整理了一个文档。 别说&#xff0c;效果还挺不错的&#xff0c;连着上线6个版本&#xff0c;都没有收…

自动化边坡监测设备是什么?

随着科技的不断进步&#xff0c;我们的生活和环境也在不断地发生变化。然而&#xff0c;自然灾害仍然是我们无法完全避免的风险。其中&#xff0c;边坡滑坡就是一种常见的自然灾害。为了保护人民的生命财产安全&#xff0c;科学家们研发出了自动化边坡监测设备。 WX-WY1 自动化…

Pytorch-RealSR超分模型

1.前言 RealSR 是一种基于学习的单图像超分辨率&#xff08;SISR&#xff09;模型&#xff0c;专门针对真实世界的图像。它由腾讯 AI 实验室于 2020 年提出。 RealSR 的核心创新是提出了一种新的退化模型&#xff0c;该模型能够更好地模拟真实世界的退化过程。该模型考虑了真实…

多臂老虎机算法步骤

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

毅速:3D打印随形冷却水路助力模具行业降本、提质、增效

随着模具行业的不断发展&#xff0c;模具制造的精度和效率已经成为企业核心竞争力的重要组成部分。为了满足市场需求&#xff0c;模具行业一直在寻求新的制造技术和方法。3D打印技术的出现&#xff0c;为模具行业带来了革命性的变革。其中&#xff0c;3D打印随形冷却水路的应用…