二分查找
二分查找的概念
写这个总结的初衷是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题:山脉数组中查找目标值
# """
# 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题:分割数组最大值
其他题还没做到,等做到再添加进来,就酱~