二分查找算法(Binary Search Algorithm):也叫做折半查找算法、对数查找算法。是一种在有序数组中查找某一特定元素的搜索算法。
注意, 有序数组中的一种搜索算法,有序数组中的一种搜索算法,有序数组中的一种搜索算法。当你看到有序数组时,就可以往二分查找这里想一想,看看能否运用。
基本思想:先确定带查找元素所在的区间范围,再逐步缩小范围,直到找到元素或找不到元素为止。
1. 每次查找从数组的中间元素开始,如果中间的元素刚好是要查找的元素,则搜索过程结束;
2. 比较中间元素与目标元素的大小,如果中间元素大,那么搜寻左边;如果中间元素小,那么搜寻右边;**从小区间的中值查找**
3. 如果某一步骤数组为空,则代表找不到
减:减少问题的规模
治:解决问题
结合就是**排除法解决问题**
每一次查找,排除掉不可能存在目标元素的区间,在剩下可能存在目标元素
的区间中继续查找。
左闭右开:即right = len(nums), 区间内的最后一个元素取不到
左闭右闭:即right = len(nums) - 1,区间内的最后一个元素可以取到
两种方式都有各自对应的代码,但相对来说,左闭右开的方式考虑的情况更加复杂,建议全部使用左闭右闭的形式
mid = (left + right) // 2:常见写法
mid = left + (right - left) // 2:防止整形溢出写法
上述的两个写法如果为偶数个数据,取得都是靠左的数据(向下取整),其实,靠右的数据也是可以取的
mid = (left + right + 1) // 2
mid = left + (right - left + 1) // 2
原因:二分查找是通过中间选择的数值来确定下次查找的区间,并不强制要求在中间
位置,靠左靠右都可以,只是在中间的效率更高
left <= right:出循环时,left = right + 1, 此时查找区间为[right+1, right],此时区间为空,返回-1
left < right:出循环时, left = right,此时查找区间为[right, right],
此时区间不为空,直接返回-1是错误的,还需要加一个判断条件
这是我们需要知道,二分查找算法的两个思路:直接找与排除法
class Solution:
def search(self, nums:List[int], target:int):
left = 0
right = len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
return -1
class Solution:
def search(self, nums, target):
left = 0
right = len(nums) - 1
while left < right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid
return left
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
if target > nums[-1]:
return len(nums)
if target < nums[0]:
return 0
while left <= right:
mid = (right + left) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
if nums[mid - 1] < target:
return mid
right = mid - 1
else:
if nums[mid + 1] > target:
return mid + 1
left = mid + 1
class Solution:
def guessNumber(self, n: int) -> int:
left = 0
right = n
while left <= right:
mid = (left + right) // 2
if guess(mid) == 0:
return mid
elif guess(mid) == 1:
left = mid + 1
else:
right = mid - 1
class Solution:
def mySqrt(self, x: int) -> int:
left, right = 0, x
while left <= right:
mid = (left + right) // 2
if mid * mid == x:
return mid
elif mid * mid < x:
if (mid + 1) ** 2 > x:
return mid
left = mid + 1
else:
if (mid - 1) ** 2 < x:
return mid - 1
right = mid - 1
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
n = len(numbers)
for i in range(n):
res = target - numbers[i]
left = i
right = n - 1
while left <= right:
mid = (left + right) // 2
if numbers[mid] == res:
if i == mid and numbers[mid + 1] == numbers[mid]:
return [i + 1, mid + 2]
return [i + 1, mid + 1]
elif numbers[mid] > res:
right = mid - 1
else:
left = mid + 1
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left = 0
right = len(numbers) - 1
while left < right:
if numbers[left] + numbers[right] == target:
return [left + 1, right + 1]
elif numbers[left] + numbers[right] > target:
right -= 1
else:
left += 1
class Solution:
def shipWithinDays(self, weights: List[int], days: int) -> int:
left = max(weights)
right = sum(weights)
while left <= right:
mid = (left + right) // 2
need = 1 # 表示需要的天数
cur = 0 # 表示当天运送物资数
for weight in weights:
if cur + weight > mid:
need += 1
cur = 0
cur += weight
if need <= days:
right = mid - 1
else:
left = mid + 1
return left
class Solution:
def firstBadVersion(self, n):
left = 1
right = n
while left <= right:
mid = (left + right) // 2
if isBadVersion(mid) == True:
right = mid - 1
else:
left = mid + 1
return left
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]:
if nums[0] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else:
if nums[mid] < target <= nums[-1]:
left = mid + 1
else:
right = mid - 1
return -1
class Solution:
def findMin(self, nums: List[int]) -> int:
left = 0
right = len(nums) - 1
while left < right:
mid = (left + right) // 2
if nums[mid] < nums[right]:
right = mid
else:
left = mid + 1
return nums[left]