1.有序数组,判断一个数是否存在于数组中,时间复杂度O(logn)
解题思路:
二分法,在有序数组中,提高时间复杂度的一个方法。
代码:
def demo(nums, target):
left,right=0,len(nums)-1
while left <= right:
mid = (left + right)/2
if target < nums[mid]:
right = mid - 1
elif target > nums[mid]:
left = mid + 1
else:
return True
return False
2.有序数组,从一个元素中翻转,判断一个数是否存在于数组中,时间复杂度O(logn)
例子:
[1, 2, 3, 4, 6, 7, 8, 10,11] ,从4这个元素,左右翻转,[6, 7, 8, 10,11,1, 2, 3, 4]
依然使用二分法,难点在于如何判断 target 在前半段还是后半段。对于无重复的数组[6, 7, 8, 10,1, 2, 3, 4],如果满足 A[begin] <= A[mid] ,即数组的第一个值小于中间值 6 < 11 ,那么可以断定前半段数组有序。如果不满足这个条件, 则说明后半段有序。因为把这个数组从中间分开后,一定至少有半个数组是有序的。然后再判断 target 是否在有序的半段中(这个很好判断),如果在,则相当于在有序数组中查找,很简单。如果不在有序的那半段,则一定在另外半段里。然后使用迭代即可把target找出。
def demo(nums, target):
left,right=0,len(nums)-1
while left <= right:
mid = (left + right)/2
# 判断mid是否为target,如果是,直接返回,如果不是,再判断target是在前半段还是后半段
if nums[mid] == target:
return mid
# 如果前半段有序
if nums[left] <= nums[mid]:
# 判断target是否在前半段,如果在,则继续遍历前半段,如果不在,则继续遍历后半段
if nums[left] <= target and target < nums[mid]:
right = mid -1
else:
left = mid + 1
# 后半段有序
else:
# 判断target是否在后半段,如果在,则继续遍历后半段,如果不在后半段,则继续遍历前半段
if nums[mid] < target and target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
3.一个递增数组中,target出现的次数
输入: nums = [5,7,7,8,8,10]
, target = 8
输出: 2
解题思路:
排序数组中的搜索问题,首先想到 二分法 解决。
排序数组 nums 中的所有数字 targettarget 形成一个窗口,记窗口的 左 / 右边界 索引分别为 left 和 right ,分别对应窗口左边 / 右边的首个元素。
本题要求统计数字 target 的出现次数,可转化为:使用二分法分别找到 左边界left 和 右边界 right ,易得数字target 的数量为right−left−1 。
class Solution:
def search(self, nums: [int], target: int) -> int:
# 搜索右边界 right
i, j = 0, len(nums) - 1
while i <= j:
m = (i + j) // 2
if nums[m] <= target: i = m + 1
else: j = m - 1
right = i
# 若数组中无 target ,则提前返回
if j >= 0 and nums[j] != target: return 0
# 搜索左边界 left
i = 0
while i <= j:
m = (i + j) // 2
if nums[m] < target: i = m + 1
else: j = m - 1
left = j
return right - left - 1