题目:
给定一个排序数组 nums 和一个目标值 var ,在数组中找到目标值,并返回其索引
如果目标值不存在数组中,返回它将会被按顺序插入的位置
题目链接:搜索插入位置
题目限制:
必须使用时间复杂度为O(log n)的算法
解题思路:
目标值只有四种情况:
1、目标值在数组所有元素之前
2、目标值等于数组某个元素
3、目标值不在数组中,需要插入到指定位置
4、目标值在数组所有元素之后
利用二分法来先求中位数,设:
左指针等于数组第一个元素,既:left = nums[0];
右指针等于数组最后一个元素,既:right = [nums[-1]];
再把目标值和中位数作对比,既:
- median = (left + (right + left)) // 2
- 左指针 <= 右指针
- var < median 时,求值范围为:[左指针,median-1]
- var > median 时,求值范围为:[median+1,右指针]
- var = median 时,满足情况2
利用中位数对比 var 值,可减少遍历元素,从而达到时间复杂度为O(log n)
实现代码:
class Solution:
def searchInsert(self, nums: List[int], var: int) -> int:
left, right = 0, len(nums) - 1 # 左指针等于nums[0],右指针等于num[-1]
while left <= right: # 当left = right 时,nums元素只剩下一个
median = left + ((right - left) // 2) # 取数组的中位数,每次判断var时,中位数会跟随条件而变动
if nums[median] < var: # 如果 var 值大于中位数时
left = median + 1 # var 值的取值范围为[median+1,right]
elif nums[median] > var: # 如果 var 值小于中位数时
right = median - 1 # var 值的取值范围为[left,median-1]
else:
return median # 以上都不满足时,var = 中位数
# 不断缩小 var 值的取值范围,直到满足 left > right 时
return right + 1 # 把 var 插入当前right元素的下一个位置
代码执行逻辑:
满足 left <= right 条件时,不断循环,直到left > right,既nums中没有等于var的值
计算当前nums的中位数位置
比较当前中位数位置的元素与var值,既筛选掉一半不符合条件的元素
若var值大,则意味着var值比当前中位数的元素还要大,把var最小值取值范围移动到中位数元素的后一位
若var值小,则意味着var值比当前中位数的元素还要小,把var最大值取值范围移动到中位数元素的前一位
若var值等于中位数的元素,意味着nums的中位数位置等于var值
每判断一次,左或右指针会移动一个位置,只要满足循环条件,就会不断缩小var值的范围,直到left > right
注意:left > right 时,意味着nums中不存在等于var的值,这时,var要么小于数组中的所有元素,要么大于数组中的所有元素
按题目要求,把var插入最后一次满足条件 left <=right 中,既:当前的right + 1
可代入nums = [0,3,5,6] 和 [1,3,5,6] 试下
结论:
双指针的用法思路是:
确定数组长度以及边界问题,罗列数据的边界情况
找到中位数的位置,通过中位数作为边界,可以减少一半的元素,既:空间复杂度O(log n)
确定遍历循环退出条件
梳理遍历循环过程中 双指针和中位数的变动及其条件
循环结束,返回所需值或数组