二分查找 -- ”单次\多次“搜索旋转数组

背景信息:

'''
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
!!! 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。


示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:
输入:nums = [1], target = 0
输出:-1

'''


单次搜索旋转数组:

# todo 场景1: "单次搜索"

def search_1(nums, target):
    left = 0
    right = len(nums) - 1

    while left <= right:  # 这里不用left < right 会报错, left <= right 确保最后一次迭代能够处理仅剩两个元素的情况
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        if nums[mid] >= nums[left]:  # 截至到[mid]都是升序,表明mid左半部分有序
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:  # 这边用else涵盖其他,避免了mid右侧不一定有序的问题 456 7123 (target:5)
                left = mid + 1
        else:  # 从[mid]向后都是升序,表明mid右半部分有序
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:  # 这边用else涵盖其他,避免了mid左侧不一定有序的问题 45671 23 (target:2)
                right = mid - 1
    return -1



# 场景1 测试用例
nums = [4, 5, 6, 7, 0, 1, 2]
target = 0
print(f"target:{target} --> index:{search_1(nums, target)}")

多次搜索旋转数组 --- 复用旋转点(pivot)K:

# todo 场景2: "多次搜索"

def findMinIndex(nums):
    left, right = 0, len(nums) - 1
    while left < right:
        mid = (left + right) // 2
        if nums[mid] > nums[right]:  # 最小值在右半部分
            left = mid + 1
        else:  # 最小值在左半部分
            right = mid 

    return left

def searchWith_K(nums, target, k):
    if not nums:
        return -1
    
    # 二分搜索
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) // 2
        # 把索引映射回原始数组(即初始的Index)--> 调整回有序状态数组 --> 二分查找
        real_mid = (mid + k) % len(nums)
        if nums[real_mid] == target:
            return real_mid
        if nums[real_mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

# 场景2 测试用例

# nums = [4, 5, 6, 7, 0, 1, 2]
nums = [0, 1, 2, 4, 5, 6, 7]
targets = [0, 5, 2, 1, 3]
k = findMinIndex(nums)  # 计算k (pivot) -- 叫旋转点也可以

print(f"旋转点:{k}")
for target in targets:
    print(searchWith_K(nums, target, k))  # 复用 k,提高效率

关键:
real_mid = (mid + k) % len(nums)  ---> 映射回原数组 ---> 有序状态 ---> 使用二分查找

二分查找 -- ”单次\多次“搜索旋转数组_第1张图片(所有数字都是Index, 带进array看)

ps.根据具体使用场景选择不同的搜索方式

你可能感兴趣的:(算法,数据结构,二分查找,旋转数组,python)