刷题时遇到一个经典问题:给定一个排序数组和一个目标值,如果找到目标值就返回其索引,否则返回它应该被插入的位置。
这个问题看似简单,但实现起来却有不少细节值得玩味。今天我们就来深入剖析这道题,看看如何从暴力解法一步步优化到最优雅的二分查找。
问题描述
给定一个0索引 的排序 数组 arr[](元素互不相同)和一个整数 k:
- 如果
k存在于数组中,返回它的索引 - 否则,返回
k应该插入的位置(插入后数组仍然保持有序)
示例:
- 输入:
arr[] = [1, 3, 5, 6], k = 5→ 输出:2(因为5在索引2处) - 输入:
arr[] = [1, 3, 5, 6], k = 2→ 输出:1(2应插入在1和3之间)
方法一:暴力遍历 - O(n) 时间,O(1) 空间
最直接的想法就是遍历数组,找到第一个大于等于 k 的元素位置。
python
def searchInsertK(arr, k):
for i in range(len(arr)):
if arr[i] >= k:
return i
return len(arr)
思路:
- 如果
arr[i] >= k,说明k应该放在i位置(要么找到,要么插入) - 如果遍历完都没找到,说明
k比所有元素都大,插入到末尾
复杂度: 最坏情况需要遍历整个数组,时间复杂度 O(n)。
刷算法题时,从O(n)暴力解法进化到O(log n)的二分查找,是不是总感觉理解不够透彻?别慌,最近挖到一个宝藏网站图码,它把60多种数据结构和算法做成交互式动画可视化,包括搜索插入位置的完整过程。更绝的是,支持输入自定义数据或上传C/C++/Java/Python代码,一键生成动画并逐步解析,连AI都能7×24小时随时解释代码逻辑。无论是备战408考研,还是数据结构期末考试复习,这个工具都能让抽象概念瞬间"活"起来。强烈建议去体验一下,绝对让你事半功倍!
图码-数据结构与算法交互式可视化平台
访问网站:https://totuma.cn
方法二:标准二分查找 - O(log n) 时间,O(1) 空间
既然数组是有序的,二分查找自然是首选。
python
def searchInsertK(arr, k):
left, right = 0, len(arr) - 1
while left <= right:
mid = left + (right - left) // 2
if arr[mid] == k:
return mid
elif arr[mid] > k:
right = mid - 1
else:
left = mid + 1
return left
核心思想:
- 使用
left和right指针缩小搜索范围 - 如果找到
k,直接返回mid - 如果没找到,循环结束时
left就是插入位置
为什么返回 left? 当 left > right 时,left 指向的是第一个大于 k 的元素位置,这正是插入点。
方法三:变种二分查找 - O(log n) 时间,O(1) 空间
这个方法更巧妙,通过调整循环条件和指针移动方式,确保 left 始终指向可能的插入位置。
python
def searchInsertK(arr, k):
left, right = 0, len(arr) - 1
while left < right:
mid = left + (right - left) // 2
if arr[mid] < k:
left = mid + 1
else:
right = mid
return left + 1 if arr[left] < k else left
精妙之处:
- 循环条件改为
left < right,避免死循环 - 当
arr[mid] < k时,left = mid + 1,排除左侧小于k的元素 - 否则
right = mid,保留当前mid作为候选 - 循环结束后,
left指向第一个大于等于k的元素 - 最后检查
arr[left]是否小于k,如果是则返回left + 1
三种方法对比
| 方法 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|
| 暴力遍历 | O(n) | O(1) | 简单直观,适合小数组 |
| 标准二分 | O(log n) | O(1) | 经典二分,易于理解 |
| 变种二分 | O(log n) | O(1) | 更优雅,边界处理更统一 |
总结
这道题看似简单,但却是二分查找的经典应用场景。从 O(n) 到 O(log n) 的优化,体现了算法设计中"利用数据特性"的重要思想。
在实际面试中,建议掌握第二种方法(标准二分),它最通用也最容易解释清楚。第三种方法可以作为进阶,展示你对二分查找边界的深入理解。
记住:当问题涉及有序数组的搜索时,二分查找永远是第一选择!