LeetCode——二分(初阶)

LeetCode——二分(初阶)

文章目录

简要介绍

二分算法是我们写算法题目中比较常见的一种优化算法,这个算法很好地体现了分治的思想,我们的二分算法不管是在算法题目中屡见不鲜,在实际工程当中也是十分热门的一个算法,比如我们的数据库索引、文件系统、甚至是我们游戏AI的路径规划都或多或少地用到了我们的二分算法,在我们的算法题目中常见的几种二分算法分别是二分答案、二分区间和二分浮点数。

二分答案

这是一个在有单调性的区间中找值的一个算法,通过二分思想来调整我们的查找范围。

二分区间

这个其实和我们的二分答案类似,只不过这里是搜索的一个区间而不是一个值,这种方法常应用于在区间内查找某一个解的情况。

二分浮点数

这个就比较冷门了,这里其实和二分答案类似,只是这里的处理方式不同而已。

相关例题

二分查找

题目描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果 target 存在返回下标,否则返回 -1

你必须编写一个具有 O(log n) 时间复杂度的算法。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 解释: 9 出现在 nums 中并且下标为 4 

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2 输出: -1 解释: 2 不存在 nums 中因此返回 -1 

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。
题目分析

这个题目是一道相当经典的一道二分答案的题目,我们这里的思想就是二分。

实现思路

定义两个指针,一个左指针left,一个右指针right,分别指向我们的数组的左右端,计算出我们的中间点mid,然后就是三种情况的讨论了:
1、arr[mid] == target说明找到了,直接返回即可。

2、arr[mid] > target说明我们的区间[mid, right]都是要大于我们的target的,所以我们要舍弃这个区间的值,在我们的左边[left, mid - 1]继续查找,也就是让我们的right = mid - 1

3、arr[mid] < target说明我们的区间[left, mid]这个区间的值都是要小于我们的target的,所以我们这里要舍弃这一段区间,在我们的区间[mid + 1, right]继续进行查找,也就是让我们的left = mid + 1

以上三个操作都是在while(left < right)条件下进行的,跳出循环说明没有找到。

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

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

题目描述

给你一个按照非递减顺序排列的整数数组 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] 

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109
题目分析

这个题目就是我们之前说的二分区间的经典例题,这里其实还是比较容易混淆的,这里我们做一个区分(使用基础二分,通过基础二分模板然后保留特定的左或是右下标的方式的做法其实是找到的值不一定是等于我们的target的,这里的就是必须要是等于target的算法)。

实现思路

我们这里的基本思路就是找到我们的左边界和我们的右边界,这是两种不同的情况,我们这里需要分开讨论着两种情况的:

找左边界:

我们这里需要明确几个区间的特点:

我们的左区间[left, k - 1]都是要小于我们的target的。

我们的右区间[k, right]都是要大于等于我们的target的。

所以这里我们的mid的位置右有了下面的两种情况:
当我们的mid[left, k - 1]的时候,也就是说arr[mid] < target,说明[left, mid]都是可以舍弃的,我们这里和我们的二分答案的更新是一样的就是将left移到mid + 1的位置上。

但是当我们这里的mid[k, right]的时候就和我们的二分答案的地方不一样了,这里我们是大于等于我的target值,我们要找的就是tatget值在的最左边的下标,所以这里的k我们是不能舍弃的,我们这里可以将我的[left + 1, right]舍弃掉,所以这里我们可以将right更新到mid位置。

需要特别注意的是(也是特别容易出错的):我们这里一定是向下取整。

示例(如果是向上取整):

我们这里图示的情况就是陷入了死循环的状态,所以这里我们采取的应该是向下取整,这里我们也是比较好理解的,相较于我们的二分答案我们这里的代码主要还是没有让我们的右指针往左靠,于是我们这里采用向下调整整体上是使得我们的mid更加向我们的left靠近了,这也就弥补了右指针没往左靠的问题。

在这里插入图片描述

我们这里分析我们的右边界也是一样的,首先还是要明确我们的区间特点:

左区间[left, k]都是要小于等于target的。

右区间[k + 1, right]都是要大于我们的target的。

我们的mid位置的情况也是两种:

当我们的mid在[left, k]区间的时候,因为这个区间是小于等于我们的target的,所以这里的mid是不能舍弃的,我们这里可以将区间[left, mid - 1]舍弃,于是这里的更新就变成了left = mid

当我们的mid[k + 1, right]的时候,说明我们的[mid, right]都是大于target的,所以这里的区间内的值都是可以舍弃的,于是这里的更新就变成了right = mid - 1

注意:我们这里的区间是要向上取整的。

我们这里还是举出一个向下取整而导致死循环的例子:这里的解释和上面类似。

在这里插入图片描述
实现代码
classSolution{public: vector<int>searchRange(vector<int>& nums,int target){int n=nums.size();if(n==0)return{-1,-1};int left=0,right=n-1;while(left<right){int mid=left+(right-left)/2;if(nums[mid]<target) left=mid+1;else right=mid;}int Left=0;if(nums[left]!=target)return{-1,-1};// 如果找到的下标对应的值不是目标值说明我们整个区间内都没有这个target直接返回{-1, -1}即可else Left=left; left=0,right=n-1;while(left<right){int mid=left+(right-left+1)/2;if(nums[mid]<=target) left=mid;else right=mid-1;}return{Left,left};}};
小技巧
这里给一个取整的小技巧,while里面有-1,我们的取整就要+1,反之则没有+1

搜索插入位置

题目描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5 输出: 2 

示例 2:

输入: nums = [1,3,5,6], target = 2 输出: 1 

示例 3:

输入: nums = [1,3,5,6], target = 7 输出: 4 

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums无重复元素升序 排列数组
  • -104 <= target <= 104
题目分析

这个题目实际上可以转换成找出大于等于目标值的最左端,这样我们就可以复用之前写的代码了。

实现思路

这里的实现思路就是上面题目中的找左边界的思路,不过这里我们可以右两种写法,也是上面提到的容易混淆的情况,这里我们其实可以使用二分的基础模板,然后标记大于等于目标值的下标即可。

实现代码
代码一
classSolution{public:intsearchInsert(vector<int>& nums,int target){int ret=-1;int left=0,right=nums.size()-1;if(nums[nums.size()-1]<target)return nums.size();while(left<right){int mid=left+(right-left)/2;if(nums[mid]<target) left=mid+1;else right=mid;}return left;}};
代码二
classSolution{public:intsearchInsert(vector<int>& a,int value){int L =0;int R = a.size()-1;if(a[a.size()-1]<value)return a.size();int index =-1;while(L <= R){int mid = L +((R - L +1)>>1);if(a[mid]>= value){ index = mid; R = mid -1;}else{ L = mid +1;}}return index;}};

x 的平方根

题目描述

给你一个非负整数 x ,计算并返回 x算术平方根

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

**注意:**不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5

示例 1:

输入:x = 4 输出:2 

示例 2:

输入:x = 8 输出:2 解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。 

提示:

  • 0 <= x <= 231 - 1
题目分析

我们这个题目其实也是可以转换一下,我们这里实际求的是小于等于根号x的最大值,于是这里我们也是可以复用我们第二题的代码的,当然了,这里也是可以复用第三题的代码二的。

实现思路

其实这里就是条件改变了一下,基本的思路和之前还是一模一样的,这里就不再赘述了。

实现代码
代码一
classSolution{public:intmySqrt(int x){int left=1,right=x;if(x<1)return0;// 边界情况while(left<right){longlong mid=left+(right-left+1)/2;if(mid*mid<=x) left=mid;else right=mid-1;}return left;}};
代码二
classSolution{public:intmySqrt(int x){int left=1,right=x;if(x<1)return0;// 边界情况int index =-1;while(left <= right){longlong mid=left+(right-left+1)/2;if(mid*mid<=x){ index = mid; left = mid +1;}else right=mid-1;}return index;}};

Read more

Clawdbot Web Chat平台部署避坑指南:Qwen3:32B代理直连常见问题解析

Clawdbot Web Chat平台部署避坑指南:Qwen3:32B代理直连常见问题解析 1. 为什么需要这份避坑指南 你是不是也遇到过这样的情况:明明照着文档一步步操作,Clawdbot界面能打开,聊天框也能输入文字,可按下回车后——光标一直转圈,半天没反应,最后弹出“连接超时”或“API调用失败”?或者更糟,页面直接白屏、控制台报一堆502 Bad Gateway、ERR_CONNECTION_REFUSED? 这不是你的环境有问题,也不是Qwen3:32B模型本身不给力。真正卡住大多数人的,是Clawdbot与本地Ollama服务之间那层看似简单、实则脆弱的代理链路:从浏览器 → Clawdbot前端 → 内部反向代理(8080端口)→ Ollama网关(18789端口)→ Qwen3:32B模型。 这份指南不讲“如何安装Ollama”,也不重复官方启动命令。它只聚焦一件事:把你在真实部署中踩过的、查日志才定位到的、搜遍论坛都找不到答案的典型断点,一条条拎出来,配上可验证的检查项和一招见效的修复方法。全文基于实际生产环境反复验证,

By Ne0inhk
总结前端三年 理想滚烫与现实的冰冷碰撞

总结前端三年 理想滚烫与现实的冰冷碰撞

大家好,我是500佰,技术宅男 目前正在前往独立开发路线,我会在这里分享关于编程技术、独立开发、技术资讯以及编程感悟等内容 6月3日的一篇《一个普通人的30岁 他经历了什么》介绍一篇自己的碎碎念、即回顾自己以前的成长经历,那么再接着说下这3年来的工作经历,2022年1月,我以一名前端新人的身份开始了职业生涯。每当看到浏览器中运行的网站、手机里流畅的APP,或是点击按钮后转动的loading图标,都会想到这些产品背后凝聚着无数开发者的心血。我既期待能成为这个创造数字世界的一员,又难免担心:自己的技术储备是否足够?会不会被身边优秀的同事远远甩在身后? 怀揣着对未来的憧憬与一丝忐忑,我正式踏入了职业生涯的第一站。 不断尝试和调整的前两年(2022 ~ 2024) 我的职业生涯始于一家颇具特色的企业。原本以为会从事移动应用或网站开发,没想到公司专注于打造一款独特产品——我们开发了一系列可复用组件,配合自主研发的拖拽式平台,能够快速搭建Web站点。这种模式与后来流行的低代码平台颇有相似之处。 作为一名Java工程师加入公司后,却发现实际工作内容与预期有较大差异。当时还不了解’前端开发’这个

By Ne0inhk

Flutter 三方库 dart_webrtc 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、基于 WebRTC 标准的工业级实时音视频通讯与低延迟流媒体引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 dart_webrtc 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、基于 WebRTC 标准的工业级实时音视频通讯与低延迟流媒体引擎 在鸿蒙(OpenHarmony)系统的跨端视频会议、分布式安防监控、直播连麦或者是需要实现“端到端(P2P)”低延迟数据传输的场景中,如何通过一套 Dart 代码调用底层浏览器级的 WebRTC 算力?dart_webrtc 为开发者提供了一套工业级的、针对 Web 平台(JS 接口)进行高度封装的 WebRTC 适配方案。本文将深入实战其在鸿蒙 Web 入口应用中的音视频能力扩展。 前言 什么是 Dart WebRTC?它不仅是一个简单的。管理过程。由于由接口包装。

By Ne0inhk
【前端】win11操作系统安装完最新版本的NodeJs运行npm install报错,提示在此系统上禁止运行脚本

【前端】win11操作系统安装完最新版本的NodeJs运行npm install报错,提示在此系统上禁止运行脚本

🌹欢迎来到《小5讲堂》🌹 🌹这是《前端》系列文章,每篇文章将以博主理解的角度展开讲解。🌹 🌹温馨提示:博主能力有限,理解水平有限,若有不对之处望指正!🌹 目录 * 前言 * 解决方案 * 方法1:以管理员身份运行 PowerShell 并更改执行策略 * 方法2:只为当前会话临时允许 * 方法3:使用命令提示符 (CMD) * 方法4:绕过策略执行单个脚本 * 推荐解决方案 * Node.js 详细介绍 * 什么是 Node.js? * 核心特点 * 1. **非阻塞 I/O 和事件驱动** * 2. **单线程但高并发** * 架构组成 * 1. **V8 JavaScript 引擎** * 2. **LibUV 库** * 3. **核心模块** * 安装与使用

By Ne0inhk