【算法】二分查找经典例题

【算法】二分查找经典例题

1.leetcode (.704)⼆分查找

1.2算法原理


二分算法的满足条件是数组有序,其实并不严谨,实际上是要具有二段性,即通过有一个数能将数组分为两部分,一次比较能筛选掉一部分

循环结束条件:

left>right,因为每个区间内的数都是未知的,即使最后left和right相等还是要根目标值比较一次,来证明一个数是否有存在

时间复杂度:


O(logN)和O(N)的效率差距很大,在int范围内,O(N)最多要比较执行4*10^9次,而O(logN)最多只要32次

数据溢出问题:

(left + right) / 2 :
在大多数情况下是安全的,但如果 left 和 right 的和超过了该类型能表示的最大值,直接相加可能会导致整数溢出
left + (right - left) / 2 可以避免这个问题,利用起始值+偏移值可以有效避免两数相加超出范围,结果相同
至于left + (right - left+1) / 2和(left + right+1) / 2要不要加1的问题,在数组数量为奇数时结果相同。不同在于数量为偶数时中间值有两个数,不加1和加1计算结果一个在左中间值一个在右中间值,并不影响最终结果

1.3总结模板

该模板为最朴素的二分查找模板,适用范围一般限制较多,后续会总结左右边界的判断模板,为万能模板,适用范围更广

1.4代码

classSolution{public:intsearch(vector<int>& nums,int target){int left=0,right=nums.size()-1;while(left<=right){int mid=(left+right)/2;if(nums[mid]<target) left=mid+1;elseif(nums[mid]>target) right=mid-1;elseif(nums[mid]==target)return mid;}return-1;}};

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


非递减顺序排列就是递增序列或不变

2.1算法原理

由于该题给定数组要么是递增要么不变,可以用一个值划分两个区间,所以具有二段性,符合使用二分算法的条件

查找区间左端点

与上题总结的朴素模板相比,该题的判断条件要发生变化。根据举例找出一般规律

如图分析:
1.当x小于target(t)的时候,由于我们寻找左端点,并且mid下标对应的值就是x,那么区间[left,mid]中的值都小于t,可以全部舍弃,将left更新到mid+1下标处
2.为什么要将>和=条件放在一起分析?因为=时只是代表有可能找到了左端点,该题并未结束,不能作为结束条件返回结果,所以一起分析因为该区间包含了左端点
3.当x>=t时,由于mid位置有可能是左端点下标所以不能舍去,左端点一定在区间[left,mid]中,将right更新为mid
  • 细节处理

1.循环条件的判断

循环条件为left<right:
数组内元素总共三种情况,存在左端点、元素全部小于目标值、全部大于目标值,当left==right时就是最终结果,无需判断。
如果判断当数组内元素全部小于目标值时left=mid+1刚好结束循环,但若元素大于等于目标值时right=mid会陷入死循环

2.求中点的操作:

1式在x<t时left=mid+1移动到right处结束循环,当x>=t时right=mid移动到left处结束循环
2式在x<t时left=mid+1移动到right+1处结束循环,但x>=t时right=mid位置不变会陷入死循环

查找区间右端点

同理通过分析例子得出一般规律

如图分析:
1.当x<=t时,区间[left,mid-1]可以舍弃掉,mid处有可能就是右端点,所以令left=mid,继续寻找
2.当x>t时,区间[mid,right]可以舍弃掉因为都大于右端点值,令right=mid-1,继续寻找
  • 细节处理
    1.循环条件
与寻找左端点相同,都是left<right

2.求中点方式


分析过程与求左端点思路相同,无非是当元素为偶数个时中间点有两个,取左还是右,当其中一个在x<=t条件下left==mid下标都未发生变化因此会陷入死循环,不再赘述。

2.2总结模板


画星部分不用特意去记,根据题意分析二段性就可以得出,求中间值的方法可以这么想,当求左端点时,当数组元素为偶数个数时中间值有两个,mid落在左边那个,对应的计算方法就不要+1,反之求右端点时mid落在中间值右边那个,计算方法要+1,循环判断条件都相同。
更万能的记忆的方法,当if条件语句中出现了减法,那么中间值的计算就要+1

2.3代码

classSolution{public: vector<int>searchRange(vector<int>& nums,int target){//判断数组为空的情况if(nums.size()==0)return{-1,-1};//判断数组是否具有target元素int n=0;for(auto e: nums)if(e==target) n++;if(n==0)return{-1,-1};//查找左端点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;}//查找右端点int l=0,r=nums.size()-1;while(l<r){int m=l+(r-l+1)/2;if(nums[m]<=target) l=m;else r=m-1;}return{left,r};//最后写左右都可以,它们最终会相遇}};

3. (.20) x 的平⽅根

在这里插入图片描述

3.1算法原理

1.暴力解法

依次枚举所有数的平方,枚举区间为整个数组不需要额外判断,当等于目标值时直接返回,大于时说明前一个为结果。需注意由于是相乘,有可能超过int的最大值,应该使用longlong类型
classSolution{public:intmySqrt(int x){// 由于两个较⼤的数相乘可能会超过 int 最⼤范围// 因此⽤ long longlonglong i =0;for(i =0; i <= x; i++){// 如果两个数相乘正好等于 x,直接返回 iif(i * i == x)return i;// 如果第⼀次出现两个数相乘⼤于 x,说明结果是前⼀个数if(i * i > x)return i -1;}// 为了处理oj题需要控制所有路径都有返回值return-1;}};

2.二分算法


可以根据目标值来划分区间,具有二段性。[0, ret] 之间的元素,平⽅之后都是⼩于等于 x 的,ret可能为目标值,令left=ret;[ret + 1, x] 之间的元素,平⽅之后都是⼤于 x 的,令right=ret-1。

如果按小于x和大于等于x的区间来划分同理,分别令left=mid+1和right=mid即可
classSolution{public:intmySqrt(int x){if(x <1)return0;// 处理边界情况int left =1, right = x;while(left < right){longlong mid = left +(right - left +1)/2;// 防溢出if(mid * mid <= x) left = mid;else right = mid -1;}return left;}};

4. (35.) 搜索插⼊位置

在这里插入图片描述

4.1算法原理


设插入位置为ret,根据题意分析二段性可划分为小于目标值t和大于等于t的区域,左右指针的移动情况如下,当mid落在[left,ret-1]区域时由于全部小于t,令left=mid+1;当mid落在[ret,right】中时由于ret可能为目标值所以令right=mid
classSolution{public:intsearchInsert(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(target>nums[right])return right+1;return left;//由于左右指针已经相遇,返回谁都可以}};

5. (852.) ⼭脉数组的峰顶索引

在这里插入图片描述

5.1算法原理

  • 1.暴力解法
峰顶的特点:⽐两侧的元素都要⼤。因此,我们可以遍历数组内的每⼀个元素,找到某⼀个元素⽐两边的元素⼤即可
classSolution{public:intpeakIndexInMountainArray(vector<int>& arr){int n = arr.size();// 遍历数组内每⼀个元素,直到找到峰顶for(int i =1; i < n -1; i++)// 峰顶满⾜的条件if(arr[i]> arr[i -1]&& arr[i]> arr[i +1])return i;// 为了处理 oj 需要控制所有路径都有返回值return-1;}};
  • 2.二分查找
虽然该题数组不有序,但具有二段性,山峰左右两边都小于山峰值,中间位置mid可以分为以下两种种情况:

1.用索引mid和mid-1进行比较


峰值mid一定得放到左边区间中,因为是和前一个元素作比较,左区间中的最后一个元素可能位峰值,若放到右边区间中更新索引时峰值可能会被跳过,mid的计算要+1
classSolution{public:intpeakIndexInMountainArray(vector<int>& arr){int left=1,right=arr.size()-2;while(left<right){int mid=left+(right-left+1)/2;if(arr[mid]>arr[mid-1]) left=mid;else right=mid-1;}return left;}};

2.用索引mid和mid+1作比较

此时将峰值放到右边区间,右边区间的第一个元素可能为峰值,更新时right=mid,left=mid+1,此时mid的计算不+1
classSolution{public:intpeakIndexInMountainArray(vector<int>& arr){int left=1,right=arr.size()-2;while(left<right){int mid=left+(right-left)/2;if(arr[mid+1]>arr[mid]) left=mid+1;else right=mid;}return left;}};

6. (153.) 寻找旋转排序数组中的最⼩值

在这里插入图片描述

6.1算法原理

由题意得数组升序,旋转后具有两段性

旋转后的数据分为这样两段,C点即为所求最小值。我们以D点作为划分值
当mid落在AB区间时不满足,left=mid+1要跳出该区间;落在CD区间时mid<=最小值,为了缩小搜寻范围令right=mid
classSolution{public:intfindMin(vector<int>& nums){int n=nums.size()-1;int left=0,right=n;while(left<right){int mid=left+(right-left)/2;if(nums[mid]>nums[n]) left=mid+1;if(nums[mid]<=nums[n]) right=mid;}return nums[right];}};

7. (173.)点名

在这里插入图片描述

7.1算法原理

二分查找

由题意得数组递增且有序,中间会少一个数,这个数就可以把数组划分为两个区间

前区间元素值对应索引,后区间元素对应索引+1,我们要求的就是后区间的第一个元素

细节问题:

若数组未被划分为两个区间,每个元素都等于其索引,那缺失值超出数组索引有效范围,返回最后索引+1
classSolution{public:inttakeAttendance(vector<int>& records){int left=0,right=records.size()-1;while(left<right){int mid=left+(right-left)/2;if(records[mid]==mid) left=mid+1;else right=mid;}//完全连续的特殊情况判断if(records[right]==right)return right+1;elsereturn right;}};
除此之外还可以尝试
1.哈希表
2.直接遍历找结果
3.位运算
4.数学(高斯求和公式)
这些方法的时间复杂度都为O(n),不如二分

Read more

OpenClaw配置GLM联网搜索 - 免费使用AI搜索功能

OpenClaw配置GLM联网搜索 - 免费使用AI搜索功能

还在为AI联网搜索头疼费?这篇文章教你实现AI联网搜索 背景 现在AI助手大火,但是大部分都不支持联网搜索。能够联网的Perplexity一个月要20美元,对个人开发者来说确实有点肉疼。 作为一个程序员,我一直在找免费或者低成本的解决方案。直到我发现OpenClaw这个开源平台,可以很方便地自定义Skill,配合智谱AI的GLM模型,实现了免费联网搜索功能。 什么是OpenClaw OpenClaw是一个开源的AI助手平台,支持: * 多个AI模型(GPT、Claude、GLM等) * 自定义Skill(技能) * 多种部署方式 * 飞书、Telegram等多平台接入 官方文档:https://github.com/openclaw/openclaw 核心思路 利用OpenClaw的自定义Skill功能,调用智谱AI的GLM模型。GLM模型支持联网搜索工具(web_search),我们只需要: 1. 申请智谱AI的API Key 2. 编写调用脚本 3. 配置到OpenClaw 详细配置步骤 第一步:申请智谱AI API Key

By Ne0inhk
人工智能:大语言模型(LLM)原理与应用实战

人工智能:大语言模型(LLM)原理与应用实战

人工智能:大语言模型(LLM)原理与应用实战 1.1 本章学习目标与重点 💡 学习目标:掌握大语言模型的核心原理、训练流程与微调方法,学会基于开源大语言模型完成定制化对话与文本生成任务。 💡 学习重点:理解大语言模型的Transformer decoder-only架构,掌握指令微调与RLHF技术,能够使用LoRA高效微调开源LLM。 1.2 大语言模型的核心概念与发展历程 1.2.1 什么是大语言模型 💡 大语言模型(Large Language Model, LLM)是参数量达到十亿级甚至万亿级的Transformer-based模型。它通过在海量文本数据上进行预训练,学习语言的语法、语义、常识和推理能力。 LLM的核心能力包括文本生成、理解、翻译、摘要、问答等。它可以处理复杂的自然语言任务,无需针对每个任务单独设计模型结构。 LLM与传统NLP模型的核心区别: * 参数量级:传统模型参数量通常在千万级,LLM参数量可达十亿到万亿级。 * 训练数据:传统模型依赖标注数据,LLM使用海量无标注文本进行预训练。 * 能力边界:传统模型只能处理单一任务,LL

By Ne0inhk
如何让多个AI进行对话

如何让多个AI进行对话

我发现了AI聊天的新玩法:让多个AI角色自己开"圆桌会议" 作为一个AI爱好者,我最近发现了一个特别有意思的工具——TAF-ChatUltra。它彻底改变了我对AI对话的认知。以前,我们只能和单个AI聊天,现在,我可以创建多个AI角色,让他们在聊天室里自己讨论,而我只需要在一旁"围观",偶尔发个引导消息,就像导演一样掌控全局。 项目地址: https://github.com/TAF-Playground/TAF-ChatUltra 停止后,输入引导消息 根据引导消息,拉回新的主题 第一次体验:看两个AI大佬"辩论" 刚打开这个工具,我就被它的界面吸引了。现代化的设计风格,卡片式布局,看起来非常舒服。系统已经贴心地为我准备了一个演示聊天室——“AI未来展望”,里面有两个预设角色:TechMaster(技术大佬)和AIGuru(AI大佬)。 我点击了"启动&

By Ne0inhk

2026年3月18日人工智能早间新闻

各位读者,早上好。今天是2026年3月18日,星期三。欢迎收看人工智能早间新闻。昨日,从英伟达GTC大会到国内产业一线,人工智能领域释放出密集信号——算力竞赛正从地面延伸至太空,智能体加速从概念走向实干,而AI与实体经济的深度融合正在催生“超级团队”与“一人公司”的新范式。 一、国内产业纵深:“人工智能+”催生“超级团队”,智能体从概念走向实干 今年的政府工作报告首次提出打造智能经济新形态,并提出“促进新一代智能终端和智能体加快推广”“支持人工智能开源社区建设”等具体路径。在3月6日举行的经济主题记者会上,国家发改委主任郑栅洁表示,将深化“人工智能+”行动,“十五五”末人工智能相关产业规模将增长到10万亿元以上。 1. AI正从根本上释放个体能力:科大讯飞董事长刘庆峰代表指出,AI正从根本上释放个体能力,带来生产力的跃升。科大讯飞内部已涌现出一批“超级团队”,团队仅凭1名产品经理加2名前端开发人员,就完成了专家评估需15人开发3个月的任务,日产10万行高质量代码。“AI能够让一个人完成过去一个团队才能做到的事。” 刘庆峰认为,未来3至5年,AI将在数字内容、科研创新等领域持续催

By Ne0inhk