基础算法合集-二分查找(三种写法)

详解二分查找的两种写法以及二分查找的六种变形 本文详解了二分查找的两种写法以及二分查找的六种变形。 阅读详情

数组如下:

[5,7,7,8,8,10]

问题:

返回有序数组中第一个>=8的数的位置,如果所有数都<8,返回数组长度

暴力做法:

遍历每个数, 询问它是否>=8 ?显然时间复杂度是O(n)

高效做法:

L和R分别指向询问的左右边界,即闭区间[L,R],M指向当前正在询问的数 (L=0, R=n-1)

5 7 7 8 8 10
L   M      R

观察上图我们很容易得出以下两个结论:

  1. 如果当前数是小于8, 由于数组是有序的, 那么当前数和当前数左边的所有数, 都是<8的
  2. 如果当前数是大于等于8, 由于数组是有序的, 那么当前数和当前数右边的所有数, 都是>=8的
如果当前数组长度是偶数的话,中间那个数的下标为(L+R)/2,
由于C++/JAVA中"/"是下取整的,所以此时的中位数是中间靠左的那个数
如果当前数组长度是奇数的话,中间那个数的下标为(L+R)/2,此时就是正中间的那个数

 第二次查询->将L更新为M+1的位置

5 7 7 8 8 10
      L M  R
对于为啥不能将L更新为M?
这是因为我们每时每刻都是在闭区间上进行处理的,如果把L改成M,那就是左开右闭区间了.
当数组中只有一个元素时,
 8
L,R
你把R更新成M,那不就死循环了吗
因为arr[4]=8>=8,所以继续执行第三次查询

 第三次查询->将R更新M-1

5 7 7 8 8 10
      LR
arr((L+R)/2)>=8

 第三次查询->将R更新M-1

5 7 7 8 8 10
    R L
其实我们不难看出,在循环过程中,L左侧的数都是<8的,R右侧的数都是>=8的
循环不变量: L-1始终是<8,R+1始终是>=8
所以在循环结束时,R+1是我们要找的答案,由于循环结束后R+1=L,所以答案也可以用L表示

题目:在排序数组中查找元素的第一个和最后一个位置

原题链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

题目描述: 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8

输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6

输出:[-1,-1]

示例 3:

输入:nums = [], target = 0

输出:[-1,-1]

按上述的思路实现(闭区间)
int lower_bound(vector<int>& nums,int target){
    int left=0; int right=nums.size()-1;
    while(left<=right){
        int mid=left+(right-left)/2;
        if(nums[mid]<target){
            left=mid+1;
        }else{
            right=mid-1;
        } 
    }
    return right+1; //return left
}
左闭右开区间的写法
5 7 7 8 8 10
L            R
此时右边界初始为6
此时上述的第三次查询,更新R=M-1时,就可以修改成R=M了
5 7 7 8 8 10
      LR
此时循环的截止条件就变为left<right,同时left==right时截止
int lower_bound2(vector<int>&nums,int target){
    int left=0; int right=nums.size();
    while(left<right){
        int mid=left+(right-left)/2;
        if(nums[mid]<target){
            left=mid+1;   //[mid+1,right]
        }else{
            right=mid;    //[left,mid)
        }
    }
    return left;  //循环结束时,left==right
}
开区间的写法
  5 7 7 8 8 10
 L            R
L和R分别初始化为-1和6
 5 7 7 8 8 10
L    M       R
第一次查询后,L更新为M
 5 7 7 8 8 10
L    M   M1   R
第二次查询后,R更新到M1
同时最后终止条件就变成了
5 7 7 8 8 10
    L R   
此时(L,R)内就没有任何元素了
所以最后R就是指向答案的
终止条件的话,L+1=R
//开区间的写法
int lower_bound3(vector<int> &nums, int target) {
    int left = -1, right = nums.size(); // 开区间 (left, right)
    while (left + 1 < right) { // 区间不为空
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid; // 范围缩小到 (mid, right)
        } else {
            right = mid; // 范围缩小到 (left, mid)
        }
        // 也可以这样写
        // (nums[mid] < target ? left : right) = mid;
    }
    return right;
}
补充: 原问题是返回有序数组中第一个>=8的数的位置,如果问题改成>8/<8/<=8,那该如何解决呢?
">" 可以看成找>=8+1=9 的情况
"<" 可以看成找>=8左边那个数 的情况 => 找到>=8的索引然后减1
"<=" 可以看成找>8左边那个数 的情况 => 找到>8的索引然后减1
以上就是所有在有序数组中进行二分查找的情况

最后我们来解决上述题目

class Solution {
public:
    //闭区间的写法
    int lower_bound(vector<int>& nums,int target){
        int left=0; int right=nums.size()-1;
        while(left<=right){
            int mid=left+(right-left)/2;
            if(nums[mid]<target){
                left=mid+1;
            }else{
                right=mid-1;
            } 
        }
        return right+1;
    }
    //开区间的写法
    int lower_bound3(vector<int> &nums, int target) {
        int left = -1, right = nums.size(); // 开区间 (left, right)
        while (left + 1 < right) { // 区间不为空
            // 循环不变量:
            // nums[left] < target
            // nums[right] >= target
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid; // 范围缩小到 (mid, right)
            } else {
                right = mid; // 范围缩小到 (left, mid)
            }
            // 也可以这样写
            // (nums[mid] < target ? left : right) = mid;
        }
        return right;
    }
    //[A,B) 区间的写法
    int lower_bound2(vector<int>&nums,int target){
        int left=0; int right=nums.size();
        while(left<right){
            int mid=left+(right-left)/2;
            if(nums[mid]<target){
                left=mid+1;   //[mid+1,right]
            }else{
                right=mid;    //[left,mid)
            }
        }
        return left;  //循环结束时,left==right
    }
        vector<int> searchRange(vector<int>& nums, int target) {
        int start=lower_bound(nums,target);
        if(start==nums.size()||nums[start]!=target){
            return {-1,-1};     //nums数组中没有target
        }
        //如果start 存在,那么end必定存在
        int end=lower_bound(nums,target+1)-1;
        return {start,end};
    }
};
start=nums.size()是left不断向右进行移动,当L=R+1,L>R终止,R其实是一直都没有移动
例子如下:
1 2 3 4 5 6 7 找>=8的数
start返回的是>=target的位置,end返回的是<=target+1的位置
根据上面提到的,<=target的情况,可以转换成找>=target+1,即lower_bound(nums,target+1), 最后再-1,就为lower_bound(nums,target+1)-1

时间复杂度的计算

每次折半就代表了一次查询,假设我们经过k次找到目标元素,n代表nums数组中元素的个数,所以n/pow(2,k)=1,k=logn

 

【算法】经典算法题合集 题目解析 算法原理 代码编写 2. 复写零 算法原理 代码编写 3. 快乐数 算法原理 代码编写 4. 盛最多水的容器 算法原理 代码编写 5. 有效三角形的个数 算法原理 代码编写 6. 查找总价格为目标值的两个商品 算法原理 代码编写 7. 三数之和 算法原理 代码编写 8. 四数之和 算法原理 代码编写 专题二:滑动窗口 1. 长度最小的子数组 算法原理 代码编写 2. 无重复字符的最长字串 算法原理 代码编写 3. 最大连续1的个数 III 算法原理 代码编写 阅读详情

相关推荐

LeetCode简单题合集 python

1672. 最富有客户的资产总量 class Solution: def maximumWealth(self, accounts: List[List[int]]) -> int: maxSum=0 #记录下最大值 for a in accounts: #遍历每个客户 if sum(a)>maxSum: #当前客户资产总和大于记录的最大值 maxSum=sum(a) retur

JM1307hhh的博客 1548

[数据结构] 二分查找 (四种写法)

数据结构 二分查找

weixin_52437323的博客 1584

蓝桥杯C语言基础题精讲:从语法到算法的实战训练路径

在编程学习与竞赛准备中,掌握C语言的核心语法和基础算法是构建扎实编程能力的基石。C语言作为一门接近底层的编程语言,其语法严谨、执行高效,是理解计算机内存管理、数据结构等核心原理的重要工具。通过解决实际问题来驱动语法学习,能够将抽象的语法规则转化为具体的编程技能,从而提升代码的严谨性和逻辑性。在工程实践中,诸如数组边界处理、字符串操作、指针应用等基础知识点,往往是程序稳定性和效率的关键。蓝桥杯竞赛中的基础题目,正是围绕这些核心概念设计的训练场,它将变量、循环、函数、结构体等知识点融入日期计算、字符串查找、排序

weixin_31236101的博客 261

【二分查找】【三种写法】——在排序数组中查找元素的第一个和最后一个位置#力扣hot100

给你一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回[-1, -1]。必须设计并实现时间复杂度为O(log n)的算法解决此问题。

weixin_47868976的博客 1424

TMS320F280049C 学习笔记21 交叉开关 Crossbar (X-BAR)

简单介绍了X-BAR。

whyorwhnt的专栏 2万+

国产安全芯片在密码算法国密体系下的性能适配策略

国密体系即国家商用密码体系,是保障信息安全的核心技术体系。自20世纪90年代末起步,国密体系经历了从无到有、从弱到强的发展历程。1999年,国家密码管理局成立,标志着国密体系的正式建立。2000年,国密SM1算法首次发布,开启了国密算法的标准化进程。此后,SM2、SM3、SM4等算法陆续推出,形成了完整的对称与非对称密码算法体系。:2010年,国密标准正式纳入国家标准体系,国密算法在金融、政务、通信等关键领域的应用得到明确规范。

qq_40552871的博客 1354

二分查找(算法详解+模板+例题)

二分法(Bisection method) 即一分为二的方法. 设[a,b]为R的闭区间. 逐次二分法就是造出如下的区间序列([an,bn]):a0=a,b0=b,且对任一自然数n,[an+1,bn+1]或者等于[an,cn],或者等于[cn,bn],其中cn表示[an,bn]的中点。

Alex_Fufu的博客 9473

PaddleOCR实战:从零开始打造专属OCR模型的完整指南

本文提供了一份从零开始使用PaddleOCR打造专属OCR模型的完整实战指南。内容涵盖环境搭建、数据标注与准备、模型训练与调优,以及最终的模型转换与部署。重点介绍了如何利用PaddleOCR进行模型训练,并整合PPOCRLabel等工具高效准备数据集,帮助开发者快速构建适用于特定场景的高精度OCR识别能力。

rl6adventurer的博客 732

【基础算法】二分查找的多种写法

在算法竞赛中,二分查找使用的频率是非常高的,对于C++选手而言,有STL中自带的lower_bound和upper_bound二分查找,可以很方便的进行二分查找。但是非C++选手、或者需要自定义多条件查找的情况需要自己写一个二分,本文对几种常见的二分查找写法进行讲解。

_Djhhh` blog 1414

Python爬虫实战:如何高效抓取拼多多商品信息、销量与价格数据,破解反爬虫挑战

**摘要:**本文介绍利用Python爬虫技术抓取拼多多商品数据(价格、销量等)的方法与应用价值。通过分析拼多多商品页面的HTML结构,使用Requests和Selenium获取数据,并应对反爬虫机制(IP代理、请求头伪装等)。数据可存储于MongoDB或CSV用于市场监控、竞品分析等场景。文章涵盖技术选型、实战代码示例及反爬策略,为电商数据分析提供实用指导。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1670

【EtherCAT实践篇一】TwinCAT 3安装、使用

FULL(XAE)安装包:包含了XAR的功能,并能进行多种语言的编程和硬件组态,通常安装在编程电脑上;XAR安装包:包含了ADS的功能,是实时运行环境,对 TwinCAT 模块进行加载、执行、管理、实时运行与调用,通常安装在PLC中;RM安装包:在装了高版本的TwinCAT 3后,发现PLC中的TwinCAT版本较低,就需要安装相应低版本的RM安装包切换版本使用(注:4024无法使用RM安装包切换到低于4022的版本,如4020/4018/4016版本。

JXES智能生态系统 5115

【算法】——二分查找合集

二分查找基础模版和进阶模版,查找元素位置,搜索插入位置,x的平方根,山脉数组的峰顶索引,寻找峰值,点名

阿华的博客 1131

「JS逆向三百例」JS逆向HIKVISION-视频监控的前端密码加密

本文详细分析了HIKVISION-海康威视监控前端登录过程中密码的加密机制,通过逆向工程解析了其 JavaScript 加密逻辑,重点研究了HIKVISION-海康威视监控的前端密码SHA256加密算法的实现。基于逆向分析结果,使用 Python 模拟了HIKVISION-海康威视监控的登录流程,最终实现了自动化登录功能。本文旨在为安全研究人员提供技术参考,帮助理解前端加密机制,并强调技术研究的合法性和合规性。

布啦啦 1095

二分查找简介、二分查找的三类模板总结,以及对应类型的算法题练习合集

声明: 模板的内容来源于力扣的探索章节,加上了自己的理解后的修改以及注释 0、介绍 二分查找是一种在每次比较之后将查找空间一分为二的算法。每次需要查找集合中的索引或元素时,都应该考虑二分查找。如果集合是无序的,我们可以总是在应用二分查找之前先对其进行排序。 二分查找一般由三个主要部分组成: 预处理 —— 如果集合未排序,则进行排序。 二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半。 后处理 —— 在剩余空间中确定可行的候选者。 先来看一个最基本的二分查找题目: 给定一个 n 个元素有

LifeGoesOn 322

如何调用 DBMS_DISKGROUP 对 ASM 文件进行随机读取

该篇博客介绍了如何通过 Java 调用 DBMS_DISKGROUP 读写 Oracle ASM 共享存储文件,并提供了 Java Demo。并测试出 DBMS_DISKGROUP 存在性能瓶颈。

我要出家当道士 1178

CenterPoint 工程复现

CenterPoint 工程复现,本文中使用版本:BEVerse工程下的mmdet3d (`MMDet3d v0.17.3`)版本,MMDetection3D中其他版本的使用`大同小异`。

Darchan的博客 5663

【ZZ】二分查找七种情况大合集

二分查找七种情况大合集,核心不变 二分查找,其实是非常简单的算法,却令自己非常容易晕头转向。早该抽取时间好好推一推,怪自己太懒。今年市赛有题卡了2小时,就是因为二分写不出。写出来样例都对,却无限WA,找不到问题,还影响心态。前一段时间好好梳理了一下,在这里贴出来,警示自己。。/(ㄒoㄒ)/。 在此之前我是借鉴了各路大佬的博客来验证自己的想法的,贴一下借鉴的大佬博客:http:...

dianjian6614的博客 306

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Leetcode二分查找合集

力扣二分查找合集(C++)

qq_43523544的博客 731

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【Leetcode 专题】二分查找

Leetcode 69. x 的平方根

JehanRio's Private Blog 837

算法训练Day01 Leetcode704.二分查找

LeetCode704.二分查找

weixin_47284299的博客 1221

查找算法合集

一、顺序搜索法由于不知道要查找元素的具体位置,只能一个元素一个元素的去判断。平均查找(n+1)/2intfind(intarray[],intlength,intvalue){if(NULL==array||0==length)return-1;for(intindex=0;index&...

weixin_34246551的博客 91

初识算法

合集 - 数据结构与算法(2)1.初识算法09-222.基础数据结构之数组09-23收起 1.1 什么是算法? 定义 在数学和计算机科学领域,算法是一系列有限的严谨指令,通常用于解决一类特定问题或执行计算 In mathematics and computer science, an algorithm (/ˈælɡərɪðəm/) is a finite sequence of rigorou...

weixin_37069417的博客 152

【精华文章】戊子仲秋的算法学习笔记汇总

会根据我的学习进度持续更新. . .

Locky136的博客 804

【灵神高频面试题合集04-05】二分查找

二分查找:04 二分查找 红蓝染色法;05 数组峰值 搜索旋转排序数组。

qq_43629945的博客 464

字符串相关算法合集

...日后会慢慢补(flag!)先来讲讲基本的 一.字符串Hash 将字符串用一个数表示,常用的写法有: 1.自然溢出 2.单Hash 3.双Hash 前两个会被精心构造的串卡掉,最后一个虽然目前卡不掉,但是出题人可以卡你常数。 所以这个算法很Naive?不是的 我们来看一道题 bzoj1014 用splay维护字符串的最长公共前缀,那么问题来了,没有一种OI比赛中常用...

223

【算法合集-leetcode系列】3. 搜索插入位置(No.35)

题目描述: 35. 搜索插入位置https://leetcode.cn/problems/search-insert-position/用普通循环方法找肯定是可以的,但题目要求复杂度O(log n),就意味着要使用二分法啦。整体思路类似于二分查找目标target,但本问题多了“插入”的情况,需注意边界条件问题。我的思路:(1)先判断左右两端临界问题。若超出数组范围,那么直接比较最大最小值,返回位置(2)当target存在于list中,直接返回(3)当target不存在鱼list中并位于中间两数之间时,解为(

qq_32793161的博客 1670
上一篇: 力扣第446场周赛
下一篇: 基础算法合集-并查集
一只鱼^_
博客等级 码龄3年 1073粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值