- 寻找旋转排序数组中的最小值 II
元素可能重复
class Solution:
def findMin(self, nums: List[int]) -> int:
left = 0
right = len(nums) - 1
while left < right:
if nums[left] < nums[right]:
return nums[left]
mid = (left + right) // 2
if nums[mid] == nums[right]:
# 这里是精髓,和二分查找不一样。另外mid和右边界比,也和二分查找不一样。
# 二分查找是直接和目标元素比
right -= 1
elif nums[mid] < nums[right]:
right = mid
else:
left = mid + 1
return nums[left]
二分查找,无重复元素,求lower_bound。
对应leetcode 35. 搜索插入位置
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)
while left < right:
mid = left + (right - left)//2
if nums[mid] == target:
return mid
if nums[mid] < target:
left = mid + 1
else:
right = mid
return left
说明:
- 算法使用了左闭右开区间。初始条件一定是[0, len(nums))。相应条件为left < right
- 注意和二分查找区别,nums[right] > target时。
right = mid而不是mid-1。 - 注意避免死循环。当mid == right时,必有left = might。这时就退出循环。
有重复元素时求下界和上界
对应leetcode 34. 在排序数组中查找元素的第一个和最后一个位置
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
lower_index = self.lower_bound(nums, 0, len(nums), target)
if lower_index == len(nums) or nums[lower_index] != target:
return [-1, -1]
upper_index = self.upper_bound(nums, lower_index + 1, len(nums), target)
return [lower_index, upper_index - 1]
def lower_bound(self, nums, left, right, target):
while left < right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid
return left
def upper_bound(self, nums, left, right, target):
while left < right:
mid = left + (right - left) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid
return left
说明:求下界和上界时,基本结构一致。区别仅在于相等时的处理。
1. 求下界,nums[mid] = target时,往左搜索。令right = mid
2. 求上界,nums[mid] = target时,往右搜索。 left = mid + 1
可以看出针对左开右闭区别,往右搜索一定是left = mid + 1.而往左搜索是right = mid。这里有着细微的差别。