题目链接:https://leetcode.com/problems/first-bad-version
解法:
二分查找。
如果一个版本为错误版本(isBadVersion为True),那么第一个错误版本在该版本左侧(包括该版本);如果一个版本为正确版本,那么第一个错误版本在该版本的右侧(不包括该版本)。
如此不断收缩范围,直到left和right指针相等。
注意,这里的判断条件是 while left < right,为啥不是 while left <= right呢?我之前二分查找统一写成左闭右闭,所以判断条件是 while left <= right ,但是这个题,right 的更新是 right = mid,而不是 right = mid + 1,那么如果判断条件加了 = 号,就可能会无限循环下去。比如 [1,2,3,4,5],版本为 [false, false, false, true, true],那么第一个错误版本是4,第一轮判断后 left=4,right=5,第二轮判断时 mid=4,那么right=mid=4,这样 while left <= right 就会一直满足,永远循环下去。
边界条件:无
时间复杂度:O(logn)
空间复杂度:O(1)
# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:
class Solution:
def firstBadVersion(self, n: int) -> int:
left, right = 1, n
while left < right:
mid = left + (right - left ) // 2
if isBadVersion(mid):
# 错误版本在 [left, mid] 这个闭区间
right = mid
else:
left = mid + 1
return left
题目链接:https://leetcode.com/problems/search-in-rotated-sorted-array
解法:
二分查找,统一写左闭右闭区间:while循环时,left <= right。
这个题,麻烦还是在于条件判断时,要不要加 == 符号,比如有时候是 <=,有时候又是 < ,非常容易搞错。我暂时也没想到好的办法。
nums[mid] >= nums[left] 这个条件里加了等号,比如 [3,1],查找1,这个例子可以验证必须加等号。
以下是官方题解的图:
其他的参考题解。
参考题解:二分查找
边界条件:
时间复杂度:O(logn)
空间复杂度:O(1)
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
# 注意这里有等号
if nums[mid] >= nums[left]:
# 正因为target不等于nums[mid],所以需要mid左移一位
if target >= nums[left] and target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else:
# 正因为target > nums[mid] 而不是相等,所以mid肯定不是target的位置,而是右移一位,于是left = mid + 1
if target > nums[mid] and target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
题目链接:https://leetcode.com/problems/time-based-key-value-store
解法:
这个题用二分查找。我们构造哈希map,值是一个列表,元素是pair对 [value, timestamp],由于timestamp是升序的,所以我们用二分查找来做。那么我们要找到小于等于目标timestamp的数中,最大的一个。
这个题很多题解都没有说明一个关键问题,那就是在二分查找中,如果target不在升序数组nums里面,那么查找结束时,left和right分别是代表什么。
举个例子 nums=[1,3,5,6,7],target=4,按照左闭右闭的二分查找写法,那么当left > right 时,查找结束,即 left=2, right = 1。可以看到 nums[left] 表示第一个大于target的数,而 nums[right] 表示小于target的数中,最大的一个。
所以这个题,如果调用get函数时,进行二分查找,如果目标timestamp不在哈希map中,那么查找结束时,left > right。如果left >0, right >=0,那么我们可以通过 left-1来获取答案,也可以通过right来获取答案。
参考题解:二分查找
边界条件:无
时间复杂度:O(logn)
空间复杂度:O(n)
class TimeMap:
def __init__(self):
self.map = {}
def set(self, key: str, value: str, timestamp: int) -> None:
if key not in self.map:
self.map[key] = []
self.map[key].append([value, timestamp])
def get(self, key: str, timestamp: int) -> str:
if key not in self.map: return ''
kv_list = self.map[key]
left, right = 0, len(kv_list) - 1
while left <= right:
mid = left + (right - left) // 2
# 如果等于,那么直接返回
if kv_list[mid][1] == timestamp:
return kv_list[mid][0]
if kv_list[mid][1] > timestamp:
right = mid - 1
else:
left = mid + 1
# 当left>right时,停止循环,那么[:right]区间是 < timestamp,[left:]区间是>timestamp的
if left > 0:
return kv_list[left-1][0]
return ''
# Your TimeMap object will be instantiated and called as such:
# obj = TimeMap()
# obj.set(key,value,timestamp)
# param_2 = obj.get(key,timestamp)