【刷题LeetCode】二分查找算法应用

二分查找的概念

写这个总结的初衷是4月29日每日一题中「山脉数组中查找目标值」,其中力扣题解对于二分查找做了总结和讲解,感觉讲的非常好,所以也想记录下来。
一般题中给出O(logN)时间复杂度或是在有序数组中进行查找,我们通常都会想到二分查找,其实二分查找不一定只能应用于有序数组中,也可以应用于部分有序、旋转排序数组、山脉数组中。
二分查找是减而治之思想的典型应用:

  • 每次都将问题的规模减少,直到问题解决
  • 减治思想是分治思想的特例
  • 减治思想的应用:双指针问题、选择排序算法、TopK问题、二分搜索树中的查找操作

思路1,在循环体中查找元素

# -*- encoding: utf-8 -*-
"""
@File    : binary_search.py
@Time    : 2020/4/30 2:46 下午
@Author  : zhengjiani
@Email   : 936089353@qq.com
@Software: PyCharm
"""
def search(nums,left,right,target):
# 在[left,right]中查找
    while left<right:
    # java等语言中为防止整型溢出,改为mid = left + (right - left)/2
        mid = (left + right) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] > target:
            # 下一轮搜索区间[left,mid-1],向左区间搜索
            right = mid - 1
        else:
            # 下一轮搜索区间[mid+1,right],向右区间搜索
            left = mid + 1
    return -1
if __name__ == '__main__':
    nums = [0,1,2,3,4,5]
    print(search(nums,0,len(nums),5))

思路1特点:

  • 在区间中只剩一个元素的时候还会执行一遍循环体
  • 在循环体内部找目标元素
  • 循环体内部的分支通常有3个

在循环体中缩小搜索区间

def search(nums,left,right,target):
    while left<right:
        # 选择中位数时下取整
        mid = left + (right-left)//2
        if check(mid):
            left = mid + 1
        else:
            right = mid - 1
def search_3(nums,left,right,target):
    while left<right:
        # 选择中位数时上取整
        mid = left + (right-left+1)//2
        if check(mid):
            right = mid - 1
        else:
            left = mid + 1

思路2特点:

  • 每个分支做的事情都是根据在不断缩小目标元素的区间
  • 当退出循环以后,区间只剩下一个元素,视情况单独判断m

1095题:山脉数组中查找目标值

prac1095

# """
# This is MountainArray's API interface.
# You should not implement it, or speculate about its implementation
# """
#class MountainArray:
#    def get(self, index: int) -> int:
#    def length(self) -> int:

def binary_search(mountain, target, l, r, key=lambda x: x):
    target = key(target)
    while l <= r:
        mid = (l + r) // 2
        cur = key(mountain.get(mid))
        if cur == target:
            return mid
        elif cur < target:
            l = mid + 1
        else:
            r = mid - 1
    return -1

class Solution:
    def findInMountainArray(self, target: int, mountain_arr: 'MountainArray') -> int:
        l, r = 0, mountain_arr.length() - 1
        # 寻找峰顶
        while l < r:
            mid = (l + r) // 2
            if mountain_arr.get(mid) < mountain_arr.get(mid + 1):
                l = mid + 1
            else:
                r = mid
        peak = l
        index = binary_search(mountain_arr, target, 0, peak)
        if index != -1:
            return index
        index = binary_search(mountain_arr, target, peak + 1, mountain_arr.length() - 1, lambda x: -x)
        return index

力扣相关题目

这些题目的区别仅在于对于二分查找中判别函数的设计上:

33、81题:搜索旋转排序数组I、II

prac33
采用思路1三分支

# -*- encoding: utf-8 -*-
"""
@File    : prac33.py
@Time    : 2020/4/27 8:20 上午
@Author  : zhengjiani
@Email   : 936089353@qq.com
@Software: PyCharm
通过比较端点数据查看子数组是否有序
"""
from typing import List


class Solution:
    """时间复杂度O(logN)"""
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        l,r =0,len(nums)-1
        while l<=r:
            mid = (l + r)//2
            if nums[mid] == target:
                return mid
            # 端点比较验证子数组是否为有序数组
            if nums[0] <= nums[mid]:
                if nums[0] <= target < nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            # 无序
            else:
                if nums[mid] < target <= nums[len(nums)-1]:
                    l = mid + 1
                else:
                    r = mid - 1
        return -1


if __name__ == '__main__':
    # nums = [4,5,6,7,0,1,2]
    # target = 0
    nums = [3,1]
    target = 1
    s = Solution()
    print(s.search(nums,target))

153、154题:寻找旋转排序数组中的最小值I、II

69题:平方根

287题:寻找重复数

875题:爱吃香蕉的珂珂

1300题:转变数组后最接近目标值的数组和

410题:分割数组最大值

其他题还没做到,等做到再添加进来,就酱~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值