LeetCode 35. 搜索插入位置:二分查找的经典应用

刷LeetCode时,二分查找是高频考点,而35题「搜索插入位置」更是二分查找的入门级经典------它不仅考察对二分查找核心逻辑的理解,还需要灵活处理"目标值不存在"的边界场景。今天就来拆解这道题的解题思路,一步步看懂代码背后的逻辑,吃透O(log n)时间复杂度的实现要点。

题目回顾

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

核心约束:必须使用时间复杂度为 O(log n) 的算法。

这里要注意,题目明确给出"排序数组",这是二分查找能够适用的前提------二分查找的核心就是利用"有序"特性,每次将查找范围缩小一半,从而实现对数级的时间复杂度,比暴力遍历(O(n))高效得多。

解题思路:二分查找的边界处理

二分查找的关键的是「定义查找区间」和「处理边界条件」,这道题的难点在于:当目标值不在数组中时,如何精准找到它的插入位置。

我们先明确核心思路:

  1. 定义左右指针,划定查找区间(本题采用「左闭右闭」区间,即left ≤ right时,区间内仍有元素可查找);

  2. 计算中间位置mid,通过比较target和numsmid的大小,缩小查找范围;

  3. 用一个变量ans记录"可能的插入位置",默认值设为数组长度n(对应目标值比所有元素都大的情况);

  4. 当target ≤ numsmid时,说明目标值可能在mid左侧(或就是mid),此时更新ans为mid,并将右指针左移(right = mid - 1);

  5. 当target > numsmid时,说明目标值在mid右侧,将左指针右移(left = mid + 1);

  6. 循环结束后,ans即为目标值的索引(存在时)或插入位置(不存在时)。

代码逐行解析

给出的代码简洁高效,我们逐行拆解,看懂每一步的作用:

typescript 复制代码
function searchInsert(nums: number[], target: number): number {
  const n = nums.length;
  let left = 0, right = n - 1, ans = n;
  // 维护两个闭区间
  while (left <= right) {
    let mid = (left + right) >> 1;
    if (target <= nums[mid]) {
      ans = mid;
      right = mid - 1;
    } else {
      left = mid + 1;
    }
  }
  return ans;
};

1. 变量初始化

  • const n = nums.length;:记录数组长度,避免多次调用nums.length,提升效率;

  • let left = 0, right = n - 1;:定义左指针(指向数组起始位置)和右指针(指向数组末尾位置),确立「左闭右闭」的查找区间;

  • ans = n;:初始化插入位置为数组长度,这是一个"兜底"设置------当target比数组中所有元素都大时,插入位置就是数组末尾,即索引n。

2. 二分查找循环

while (left <= right):循环条件是左指针不大于右指针,这是「左闭右闭」区间的核心特征------当left == right时,区间内还有最后一个元素需要判断,避免遗漏。

3. 中间位置计算

let mid = (left + right) >> 1;:计算中间索引mid,这里用「右移1位」代替「除以2」,效率更高(二进制右移1位等价于整除2,且计算机处理位运算比除法更快)。

注意:也可以写成mid = Math.floor((left + right) / 2),两者效果一致,但右移写法更简洁高效。

4. 核心判断逻辑

这部分是整个算法的核心,负责缩小查找范围并更新插入位置:

  • if (target <= nums[mid]):

如果目标值小于等于中间元素,说明目标值要么就是numsmid,要么在mid的左侧区间。此时,我们将ans更新为mid(因为mid是当前可能的插入位置),然后将右指针right移到mid - 1,缩小查找范围到左半部分。

  • else:

如果目标值大于中间元素,说明目标值在mid的右侧区间,此时不需要更新ans(因为当前mid不可能是插入位置),直接将左指针left移到mid + 1,缩小查找范围到右半部分。

5. 返回结果

return ans;:循环结束后,left会大于right,此时ans已经记录了目标值的索引(存在时)或正确的插入位置(不存在时)。

关键细节:为什么这样能找到插入位置?

很多人会疑惑,ans的更新逻辑为什么能精准定位插入位置?我们举两个例子理解:

  1. 示例1:nums = 1,3,5,6,target = 5(目标值存在)

循环过程中,当mid = 2(numsmid = 5),target <= numsmid,ans更新为2,right = 1。之后循环结束,返回ans=2,正确。

  1. 示例2:nums = 1,3,5,6,target = 2(目标值不存在)
  • 初始left=0,right=3,ans=4;

  • mid=1(numsmid=3),target=2 <= 3,ans更新为1,right=0;

  • mid=0(numsmid=1),target=2 > 1,left=1;

  • 循环结束(left=1 > right=0),返回ans=1,即插入位置在1和3之间,正确。

本质上,ans始终记录着"当前最接近target且大于等于target的元素索引",当循环结束时,这个索引就是插入位置(如果target不存在),或者就是target本身的索引(如果存在)。

总结

这道题的核心是「二分查找的边界处理」,重点掌握3点:

  • 「左闭右闭」区间的定义的循环条件(left <= right);

  • ans变量的作用------兜底插入位置,动态更新可能的插入点;

  • mid的计算方式(右移1位)和指针移动逻辑(缩小一半范围)。

二分查找的难点在于边界条件的处理,这道题作为入门题,能帮我们快速掌握二分查找的核心逻辑。记住:只要数组有序,就可以优先考虑二分查找,而「左闭右闭」区间是最容易理解和实现的方式之一。

相关推荐
强哥聊算力6 小时前
《算力江湖·英伟达传奇》第七集:ChatGPT出现前夜,英伟达发生了什么?
服务器·人工智能·算法·ai·chatgpt·gpu算力
Jmyd01239 小时前
Web端3D营销展厅技术实现路径:从素材上云到线索回流的数据链路
前端·3d·性能优化·三维数字化
砚凝霜9 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全
濮水大叔10 小时前
NestJS Dynamic Providers 与 Cabloy/Vona Provider Scene:同接口多实现的两种装配哲学
typescript·node.js·nestjs
Navigator_Z10 小时前
LeetCode //C - 1289. Minimum Falling Path Sum II
c语言·算法·leetcode
CCblandy10 小时前
令牌桶在 0.9 倍负载超发 19%、固定窗口边界放行 20 个:四种限流算法同构实测
后端·python·算法·性能优化·架构
mmmmath_311 小时前
LeetCode.104.二叉树的最大深度
数据结构·leetcode
u01110267511 小时前
JPG、PNG、WebP 怎么选?按透明度、体积和使用场景做判断
前端·javascript·vue.js·图像处理·ai作画
jerrywus11 小时前
让 AI 写完代码后交一份报告:我改造了 answer-me-with-html
前端·claude·cursor
YangYang9YangYan12 小时前
2027供应链计划校招:数学与应用数学专业如何把预测模型讲成业务决策?
人工智能·sql·算法