前言
二分查找法是数组里面常用的方法,必须彻底掌握
一、题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素
示例1:
输入:[1,3,5,6], 5
输出:2
示例2:
输入:[1,3,5,6], 7
输出:4
二、解题思路
1.分析所有可能出现的情况
- 目标值在数组所有的元素之前
- 目标值等于数组中的某一个元素
- 目标值插入数组中的位置
- 目标值在所有元素之后
2.暴力解法
暴力解题有时候不一定时间消耗就非常高,关键看实现方式,就像是二分法查找时间消耗不一定就很定。
class solution:
def searchInsert(self, nums, target):
for i in range(len(nums)):
if (nums[i] >= target):
return i
return len(nums)
时间复杂度:O(n)
空间复杂度:O(1)
3.二分法
这道题的前提是数组有序的,而这也是使用二分查找的基础条件
只要看到面试题里面给出的数组是有序数组,都可以思考一下是否可以使用二分法解决
3.1第一种写法
target在左闭右闭区间里
class solution:
def searchInsert(self, nums, target):
n = len(nums)
left = 0
right = n - 1 #定义target在左闭右闭的区间里[left, right]
while (left <= right): #当left = right时 区间依然有效
middle = int(left + (right - left) / 2) #防止溢出 等同于 (left + right) / 2
if (nums[middle] > target):
right = middle - 1 #target在左区间,所以[left, middle - 1]
elif (nums[middle] < target):
left = middle + 1 #target在右区间,所以[middle + 1, right]
else: #nums[middle] == target
return middle
#目标值在所有元素之前[0, -1]
#目标值等于数组中的某一个元素 return middle
#目标值插入数组中的位置 [left, right], return right + 1
#目标值在所有的元素之后 [left, right], return right + 1
return right + 1
时间复杂度:O(logn)
空间复杂度:O(1)
3.2第二种写法
target在左闭右开区间里
class solution:
def searchInsert(self, nums, target):
n = len(nums)
left = 0
right = n #定义target在左闭右闭的区间里[left, right]
while (left < right): #当left = right时 区间无效
middle = int(left + (right - left) / 2) #防止溢出 等同于 (left + right) / 2
if (nums[middle] > target):
right = middle #target在左区间,所以[left, middle)
elif (nums[middle] < target):
left = middle + 1 #target在右区间,所以[middle + 1, right)
else: #nums[middle] == target
return middle
#目标值在所有元素之前[0, -1]
#目标值等于数组中的某一个元素 return middle
#目标值插入数组中的位置 [left, right), return right
#目标值在所有的元素之后 [left, right), return right
return right
时间复杂度:O(logn)
空间复杂度:O(1)
总结
每次看到二分法,一看就会一些就废,主要是因为区间定义不清楚
确定要查找的区间到底是左闭右闭,还是坐闭右开,这就是不变量。
然后在二分查找的循环中,坚持循环不变量的原则