二.二分查找(排序旋转数组)
原创
©著作权归作者所有:来自51CTO博客作者wx639033c32a1c9的原创作品,请联系作者获取转载授权,否则将追究法律责任
33. 搜索旋转排序数组
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums: return -1
l, r = 0, len(nums) - 1
# 无重复数,在有序区间查找
while l <= r:
m = (l + r) // 2
if nums[m] == target: return m
# 左半部分是有序
if nums[l] <= nums[m]:
# target落在左半部分有序区域内
if nums[l] <= target < nums[m]:
r = m - 1
else:
l = m + 1
else: # 右半部分是有序
# target落在右半部分有序区域内
if nums[m] < target <= nums[len(nums) - 1]:
l = m + 1
else:
r = m - 1
return -1
剑指 Offer II 069. 山峰数组的顶部
class Solution:
def peakIndexInMountainArray(self, arr: List[int]) -> int:
arr.insert(0,float('-inf'))
arr.append(float('+inf'))
for i in range(len(arr)):
if arr[i]>arr[i-1] and arr[i]>arr[i+1]:
return i-1
81. 搜索旋转排序数组 II
class Solution:
def search(self, nums: List[int], target: int) -> bool:
if not nums: return False
if len(nums) == 1: return nums[0] == target
l, r = 0, len(nums) - 1
while l <= r:
m = (l + r) // 2
if nums[m] == target: return True
# 有重复数, 去掉一组重复元素
# 对于数组中有重复元素的情况,二分查找时可能会有a[l]=a[m]=a[r],无法判断[l,m],[m+1,r]中哪个区间有序,需要先对当前二分区间缩小,即二分区间的左边界加一,右边界减一
if nums[l] == nums[m] and nums[m] == nums[r]:
l += 1; r -= 1
elif nums[l] <= nums[m]:
if nums[l] <= target < nums[m]:
r = m - 1
else:
l = m + 1
else:
if nums[m] < target <= nums[len(nums) - 1]:
l = m + 1
else:
r = m - 1
return False
面试题 10.03. 搜索旋转数组
class Solution:
def search(self, arr: List[int], target: int) -> int:
if not arr: return -1
l = 0; r = len(arr) - 1
while l <= r:
# 当left符合时直接返回, 因为找的是最小的索引
if arr[l] == target: return l
m = (l + r) // 2
# 当中间值等于目标值,将右边界移到中间,因为左边可能还有相等的值
if arr[m] == target: r = m
elif arr[l] < arr[m]:
if arr[l] <= target < arr[m]:
r = m - 1
else:
l = m + 1
elif arr[l] > arr[m]:
if arr[m] < target <= arr[len(arr)-1]:
l = m + 1
else:
r = m - 1
else:
l += 1
return -1
153. 寻找旋转排序数组中的最小值
class Solution:
def findMin(self, nums: List[int]) -> int:
# 模板2
# 无重复值
l, r = 0 ,len(nums)-1
# 这里控制条件没取等号,取等号大多是为了在while中直return m,不取等号就跳出while return l
while l < r:
m = (l + r) // 2
# 中间数字大于右边数字,比如[3,4,5,1,2],则左侧是有序上升的,最小值在右侧
if nums[m] > nums[r]:
l = m + 1
else: # 中间数字小于等于右边数字,比如[6,7,1,2,3,4,5],则右侧是有序上升的,最小值在左侧
r = m
return nums[l]
154. 寻找旋转排序数组中的最小值 II
剑指 Offer 11. 旋转数组的最小数字
class Solution:
def findMin(self, nums: List[int]) -> int:
# 模板2
# 有重复值
l, r = 0 ,len(nums)-1
# 这里控制条件没取等号,取等号大多是为了在while中直return m,不取等号就跳出while return l
while l < r:
m = (l + r) // 2
# 中间数字大于右边数字,比如[3,4,5,1,2],则左侧是有序上升的,最小值在右侧
if nums[m] > nums[r]:
l = m + 1
elif nums[m] < nums[r]: # 中间数字小于等于右边数字,比如[6,7,1,2,3,4,5],则右侧是有序上升的,最小值在左侧
r = m
else:
# 中间数字等于右边数字,比如[2,3,1,1,1]或者[4,1,2,3,3,3,3]
# 则重复数字可能为最小值,也可能最小值在重复值的左侧
# 所以将right左移一位
r -= 1
return nums[l]