给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
题目链接:
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-insert-position
最后的目标:在一个有序数组中找第一个大于等于 target \textit{target} target 的下标。
问题转化到这里,直接套用二分法即可,即不断用二分法逼近查找第一个大于等于 target \textit{target} target 的下标 。
复杂度分析
时间复杂度: O ( log n ) O(\log n) O(logn),其中 n n n 为数组的长度。二分查找所需的时间复杂度为 O ( log n ) O(\log n) O(logn)。
空间复杂度: O ( 1 ) O(1) O(1)。我们只需要常数空间存放若干变量。
class Solution(object):
def searchInsert(self, nums, target):
n = len(nums)
if target > nums[-1]:
return n
left, right = 0, n-1
ans = -1
while left <= right:
mid = (left + right) // 2
if target <= nums[mid]:
right = mid - 1
ans = mid
else:
left = mid + 1
return ans
二分查找的思想:
减而治之
,即将大规模问题转化成小规模问题。减而治之是分而治之
的特例,将大问题划分成若干个子问题以后,最终答案只在其中一个子问题里。
如何避免死循环(重难点)?
根据 mid 被分到左边区间
还是右边区间
,代码写出来只有以下 2 种:
边界收缩行为 1: mid 被分到左边。即区间被分成 [left, mid]
与 [mid + 1, right]
,这里用「闭区间」表示区间端点可以取到。
边界收缩行为 2: mid 被分到右边。即区间被分成 [left, mid - 1]
与 [mid, right]
。
面对上面的边界收缩行为 2(mid 被分到右边),在待搜索区间收缩到只剩下 2 个元素的时候,就有可能造成死循环。搜索区间不能缩小,是造成死循环的原因。
所以要把上面「边界收缩行为」对应的中间数取法补上。
边界收缩行为 1: mid 被分到左边。即区间被分成 [left, mid] 与 [mid + 1, right],此时取中间数的时候下取整。
mid = left + (right - left) / 2
边界收缩行为 2: mid 被分到右边。即区间被分成 [left, mid - 1] 与 [mid, right],此时取中间数的时候上取整。
mid = left + (right - left + 1) / 2
记住下面这条规则:在 if else 语句里面只要出现 left = mid 的时候,把去中间数行为改成上取整即可。
参考链接:
LeetCode上的一个很优秀的题解+总结
A u t h o r : C h i e r Author: Chier Author:Chier