二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法,前提是数据结构必须先排好序,可以在数据规模的对数时间复杂度内完成查找。但是,二分查找要求线性表具有有随机访问的特点(例如数组),也要求线性表能够根据中间元素的特点推测它两侧元素的性质,以达到缩减问题规模的效果。
用的是左闭右闭区间,所以 l e f t = m i d + 1 ; r i g h t = m i d − 1 ; left=mid+1;\:right=mid-1; left=mid+1;right=mid−1;
class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
left, right = 0, n-1
while left <= right:#注意这里是闭区间
mid = (left + right) // 2
if nums[mid] == target: return mid
if nums[mid] > target:
right = mid - 1#闭区间
else:
left = mid + 1#闭区间
return -1
使用左闭右开,可以找到小于等于目标值的索引。 r i g h t = n right=n right=n表明开区间。
class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
left, right = 0, n
while left < right:#注意这里是开区间
mid = (left + right) // 2
if nums[mid] <= target:
left = mid + 1#闭区间
else:
right = mid#开区间
return left-1
使用左闭右闭的区间。首先寻找左边界
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums: return 0
n = len(nums)
left, right = 0, n-1
if target < nums[0] or target > nums[-1]: return 0#值不在范围内,排除
while left <= right:#首先搜索左边界
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
right = mid - 1
if nums[right + 1] == target:#目标值在数组最后,或者数组首部的情况
start = right + 1
else:
return 0
同理,搜索右边界也是一样的,只不过在找到目标值之后,要将区间右移查找,即增大左区间: l e f t = m i d + 1 left=mid+1 left=mid+1
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums: return 0
n = len(nums)
left, right = 0, n-1
if target < nums[0] or target > nums[-1]: return 0#值不在范围内,排除
left, right = 0, n-1
while left <= right:#再搜索右边界
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
if nums[left - 1] == target:
end = left - 1
else:
return 0
return end - start + 1
题目:给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。你可以假设 nums1 和 nums2 不会同时为空。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int])->float:
n1 = len(nums1)
n2 = len(nums2)
if n1 > n2:
nums1, nums2 = nums2, nums1
n1, n2 = n2, n1
left, right = 0, n1
while left < right:#进行二分查找
i = (left + right) // 2
j = (n1 + n2 + 1)//2 - i#这里多加一个1,意味着左边个数等于,或者比右边个数多个1
if nums1[i] < nums2[j-1]:
left = i + 1
else:
right = i
i = left
j = (n1 + n2 +1) // 2 -i
#左边越界的时候取负无穷
m1 = nums1[i-1] if i > 0 else -float('inf')
m2 = nums2[j-1] if j > 0 else -float('inf')
if (n1+n2) % 2 == 1:
return max(m1, m2)#为奇数个的时候, 取左边界的最大值
#右边越界的时候取正无穷
m3 = nums1[i] if i < n1 else float('inf')
m4 = nums2[j] if j < n2 else float('inf')
if (n1+n2) % 2 == 0:#为偶数个的时候,取左边界的最大值,右边界的最小值,两者求一个平均
return (max(m1, m2)+min(m3,m4)) / 2
题目:给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。整数除法的结果应当截去(truncate)其小数部分。
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
#将dividend和divisor转为正数,并确定商的符号
divd,div = abs(dividend),abs(divisor)
sig = (dividend>=0) ^ (divisor<=0)#取异或,True为正,False为负
if divd >= 2**31 and div==1 and not sig :return -2**31
if divd >= 2**31 and div==1 and sig:return 2**31-1
#二分查找(查找1-divd中)
left = 0
right = divd
while left<right:#左闭右开
mid = left + (right-left+1)//2
if mid*div <= divd:#[mid,right]
left = mid
else:#[0,mid)
right = mid-1
return left if sig else -left
题目:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。
分析:升序序列直接就是对目标值进行判断在左边还是在右边。但是移位后的序列,因为数据的分布原因,不能说直接根据中间值的大小判断目标值在左边或者在右边,所以需要进行判断,中间的那个数值是属于左列表还是属于右列表。
例如:[ 0,1,2,3,4] 升序;[3,4,0,1,2] 右列表占优;[2,3,4,0,1] 左列表占优。可以使用mid 和right 比较判断是左占优还是右占优。
解题模板:
class Solution:
def search(self, nums: List[int], target: int) -> int:
count = len(nums)
if count == 0: return -1#列表为空,返回-1
left, right = 0, count-1#左闭右闭
while left <= right: #循环跳出条件
mid = (left + right) // 2 #计算中值
if nums[mid] == target: #中间值刚好等于目标值,则输出
return mid
if nums[left] <= nums[mid]:#原始列表为左占优(包含升序列表)
if nums[left]<= target < nums[mid]:#如果目标在列表左边,由于mid已经判断过了
right = mid - 1
else:#如果目标在列表右边
left = mid + 1
else:#列表的顺序为移位后的顺序
if nums[mid] < target <= nums[right]:#判断目标是否在列表右边,右占优
left = mid + 1
else:#目标是否在列表左边
right = mid - 1
return -1
题目:给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。你的算法时间复杂度必须是 O(log n) 级别。
class Solution:
def searchRange(self, nums: List[int], target: int) -> int:
if not nums: return [-1,-1]
n = len(nums)
left, right = 0, n-1
if target < nums[0] or target > nums[-1]: return [-1,-1]
while left <= right:#首先搜索左边界
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
right = mid - 1
if nums[right + 1] == target:
start = right + 1
else:
return [-1, -1]
left, right = 0, n-1
while left <= right:#再搜索右边界
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
if nums[left - 1] == target:
end = left - 1
else:
return [-1,-1]
return [start, end]
题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
count = len(nums)
left, right = 0, count-1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
题目:实现 pow(x, n) ,即计算 x 的 n 次幂函数。
class Solution:
def myPow(self, x: float, n: int) -> float:
if n == 0 or x == 1.0:return 1
if (n > 0 and n % 2 == 0): return self.myPow(x * x, n // 2)#折半计算
elif (n > 0): return self.myPow(x, n-1) * x# n为奇数,逐个计算,但是下一次就会为偶数
else: return 1/self.myPow(x, -n)#当 n 为负数的时候
题目:实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
class Solution:
def mySqrt(self, x: int) -> int:
if x == 1: return x
left, right = 1, x
while left < right :#左闭右开
mid = (left + right) // 2
m = mid * mid
if m == x: return mid
if m > x: right = mid
else: left = mid + 1
return left - 1
题目:编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:每行中的整数从左到右按升序排列。每行的第一个整数大于前一行的最后一个整数。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
if not matrix or not matrix[0]: return False
m, n = len(matrix), len(matrix[0])
top, down = 0, m-1
while top < down:#二分法,这里取的是左边界
mid = (top + down + 1) // 2#注意这里多加了一个1
if matrix[mid][0] > target:
down = mid - 1
else:
top = mid
left, right = 0, n-1
while left < right:#这里取得是右边界
mid = (left + right) // 2
if matrix[top][mid] < target:
left = mid + 1
else:
right = mid
return (matrix[top][left] == target)
题目:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
class Solution:
def search(self, nums: List[int], target: int) -> bool:
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if target == nums[mid]: return True
if nums[mid] < nums[right]:#右占优
if nums[mid] < target <= nums[right]:#目标值在右边
left = mid + 1
else:
right = mid - 1
elif nums[mid] > nums[right]:#左边占优
if nums[left] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else:#中右相等,此时缩小区间
right -= 1
return False
题目:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请找出其中最小的元素。你可以假设数组中不存在重复元素。
class Solution:
def search(self, nums: List[int], target: int) -> bool:
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if target == nums[mid]: return True
if nums[mid] < nums[right]:#右占优
if nums[mid] < target <= nums[right]:#目标值在右边
left = mid + 1
else:
right = mid - 1
elif nums[mid] > nums[right]:#左边占优
if nums[left] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else:#中右相等,此时缩小区间
right -= 1
return False
题目:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请找出其中最小的元素。注意数组中可能存在重复的元素。
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums)-1
while left < right:
mid = (left + right) // 2
if nums[mid] > nums[right]:#区间右移
left = mid + 1
elif nums[mid] < nums[right]:#区间左移
right = mid
else:#中,右相等,缩小区间
right -= 1
return nums[left]
题目:峰值元素是指其值大于左右相邻值的元素。给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
nums = [-float('inf')] + nums + [-float('inf')] #左右加负无穷
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] > nums[mid-1] and nums[mid] > nums[mid+1]:return mid - 1
if nums[mid-1] < nums[mid] < nums[mid+1]:#递增数列,峰值在右
left = mid + 1
else:#其他情况峰值在左
right = mid
题目:给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
if not nums:
return 0
n = len(nums)
ans = n + 1
sums = [0]
for i in range(n):
sums.append(sums[-1] + nums[i])
for i in range(1, n + 1):
target = s + sums[i - 1]
bound = bisect.bisect_left(sums, target)
if bound != len(sums):
ans = min(ans, bound - (i - 1))
return 0 if ans == n + 1 else ans
题目:编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:每行的元素从左到右升序排列。每列的元素从上到下升序排列。
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if not matrix: return False
m, n = len(matrix), len(matrix[0])
i, j = 0, n-1
while i < m and j >= 0:
if matrix[i][j] == target:
return True
if matrix[i][j]> target:
j -= 1
else:
i += 1
return False
题目:给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
left, right = 0, len(nums)-1
while left < right:#每次找一个中位数
mid = (left + right) // 2
cnt = 0#计算得到比中位数小于等于的值的个数
for num in nums:
if num <= mid:
cnt += 1
if cnt > mid:#如果比mid小于等于的值的个数大于mid,那么重复的数肯定在mid左边
right = mid
else:#否则,重复的数在mid右边
left = mid + 1
return left#最后弹出left,就是为重复值
题目:给定一个无序的整数数组,找到其中最长上升子序列的长度。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
size = len(nums)
# 特判
if size < 2:
return size
# 为了防止后序逻辑发生数组索引越界,先把第 1 个数放进去
tail = [nums[0]]
for i in range(1, size):
# 【逻辑 1】比 tail 数组实际有效的末尾的那个元素还大
# 先尝试是否可以接在末尾
if nums[i] > tail[-1]:
tail.append(nums[i])
continue
# 使用二分查找法,在有序数组 tail 中
# 找到第 1 个大于等于 nums[i] 的元素,尝试让那个元素更小
left = 0
right = len(tail) - 1
while left < right:
# 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解
# mid = left + (right - left) // 2
mid = (left + right) >> 1
if tail[mid] < nums[i]:
# 中位数肯定不是要找的数,把它写在分支的前面
left = mid + 1
else:
right = mid
# 走到这里是因为【逻辑 1】的反面,因此一定能找到第 1 个大于等于 nums[i] 的元素,因此无需再单独判断
tail[left] = nums[i]
return len(tail)
题目:给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质:counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
from typing import List
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
if not nums: return []#计算为空的情况,直接返回
nums.reverse()#翻转原始列表
n = len(nums)
res = [0] #因为最后一个数字比它小的肯定是 0 个
count = [nums[0]]#将第一个数字写入计数的列表
for key in range(1, n):#计算每一个字符比它小的数字个数,使用二分查找
start = 0
end = key-1
#while循环内二分查找出当前数字应该排的位置
while start <= end:
mid = (start + end) // 2
if nums[key] > count[mid]:
start = mid + 1
else:
end = mid - 1
#找出当前数字应该排的位置后插入进排序列表
count.insert(start, nums[key])
res.append(start)#写入当前数字的比它小的值
# print(res)
res.reverse()
return res
if __name__ == "__main__":
s = Solution()
print(s.countSmaller([5,2,6,1]))
题目:给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
class Solution:
def splitArray(self, nums: List[int], m: int) -> int:
def check(x: int) -> bool:
total, cnt = 0, 1
for num in nums:
if total + num > x:
cnt += 1
total = num
else:
total += num
return cnt <= m
left = max(nums)#下界
right = sum(nums)#上界
while left < right:#二分法,在上界和下界之间找一个值
mid = (left + right) // 2
if check(mid):
right = mid
else:
left = mid + 1
return left
题目:给定一个排序好的数组,两个整数 k 和 x,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。如果有两个数与 x 的差值一样,优先选择数值较小的那个数。
class Solution:
def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]:
size = len(arr)
left = 0
right = size - k
while left < right:
# mid = left + (right - left) // 2
mid = (left + right) >> 1
# 尝试从长度为 k + 1 的连续子区间删除一个元素
# 从而定位左区间端点的边界值
if x - arr[mid] > arr[mid + k] - x:
left = mid + 1
else:
right = mid
return arr[left:left + k]
题目:给定一个整数数组,返回所有数对之间的第 k 个最小距离。一对 (A, B) 的距离被定义为 A 和 B 之间的绝对差值。
class Solution:
def smallestDistancePair(self, nums: List[int], k: int) -> int:
nums.sort()
left, right = 0, nums[-1] - nums[0]#计算出距离差值k的左右范围
while left < right:
mid = (left + right) // 2
cnt, start = 0, 0
for i in range(len(nums)):
while nums[i] - nums[start] > mid:#找出距离差值小于距离设定中值的个数
#[s, i]的距离大于mid, s增加。当[s, i]距离小于mid,说明[s+1,i],[s+2, i]...
#之间所有距离都会小于mid,他们总的个数为 i-start 个
start += 1
cnt += i - start#统计小于mid的个数有多少个
if cnt < k:#cnt个数偏小,mid需要右移
left = mid + 1
else:
right = mid
return left
题目:珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。
class Solution:
def minEatingSpeed(self, piles: List[int], H: int) -> int:
left, right = 1, max(piles)+1
while left < right:
mid = (left + right) // 2
cnt = 0
for i in range(len(piles)):#计算需要的小时数
if piles[i] > mid:
cnt += (piles[i] // mid)
if piles[i] % mid != 0:#有余数不能除尽,增加一个小时
cnt += 1
else:#不足一个小时的量的,增加一个小时
cnt += 1
if cnt > H:#时间用的比较多,加大进食量
left = mid + 1
else:#时间用的少,可以减少进食量
right = mid
return left
题目:在选举中,第 i 张票是在时间为 times[i] 时投给 persons[i] 的。现在,我们想要实现下面的查询函数: TopVotedCandidate.q(int t) 将返回在 t 时刻主导选举的候选人的编号。在 t 时刻投出的选票也将被计入我们的查询之中。在平局的情况下,最近获得投票的候选人将会获胜。[0,1,1,0,0,1,0], [0,5,10,15,20,25,30]表示:第 0 分钟投给 0 号; 第 5 分钟投给 1 号; 第 10 分钟投给 1 号; 第 15 分钟投给 0 号; 第 20 分钟投给 0 号; 第 25 分钟投给 1 号; 第 30 分钟投给 0 号;
class TopVotedCandidate:
def __init__(self, persons: List[int], times: List[int]):
self.n = len(times)
self.times = times
self.memo = {}
self.win = [[] for _ in range(self.n)]
dic = {}
maxcnt = 0
for i in range(self.n):#找到每一个时刻的优胜者是谁
if persons[i] not in dic:
dic[persons[i]] = 1
else:
dic[persons[i]] += 1
if dic[persons[i]] >= maxcnt:#大于等于,因为票数相同,优胜者为最近的得票者
maxcnt = dic[persons[i]]
self.win[i] = persons[i]
else:#优胜者没变
self.win[i] = self.win[i-1]
# print(self.win)
def q(self, t: int) -> int:
left, right = 0, self.n
if t in self.memo:
return self.memo[t]
while left < right:#进行二人分查找
mid = (left+right)//2
if self.times[mid] == t:#找到时间后弹出
left = mid+1
break
if self.times[mid] > t:
right = mid
else:
left = mid + 1
self.memo[t] = self.win[left-1]
return self.memo[t]
# Your TopVotedCandidate object will be instantiated and called as such:
# obj = TopVotedCandidate(persons, times)
# param_1 = obj.q(t)
题目:传送带上的包裹必须在 D 天内从一个港口运送到另一个港口。传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。返回能在 D 天内将传送带上的所有包裹送达的船的最低运载能力。
分析:类似于珂珂吃香蕉,使用二分法。使用二分法,计算一个mid运载能力,然后根据运载能力算出总共需要的天数。
细节:
解题模板:
class Solution:
def shipWithinDays(self, weights: List[int], D: int) -> int:
n = len(weights)
left, right = max(weights), sum(weights)#计算上下界
while left < right:#开始进行二分查找
mid = (left + right) // 2
cnt, res = 0, 0
for i in range(n):#每次都对货物进行一次遍历,找出需要的天数
if weights[i] == mid and res != 0:
cnt += 2
res = 0
continue
elif weights[i] == mid and res == 0:
cnt += 1
continue
elif weights[i] < mid and res + weights[i] < mid:
res += weights[i]
continue
elif weights[i] < mid and res + weights[i] == mid:
cnt += 1
res = 0
continue
elif weights[i] < mid and res + weights[i] > mid:
cnt += 1
res = weights[i]
continue
#elif weights[i] > mid:
#cnt = float('inf')
if res != 0: cnt += 1#最后一天还有剩余,再送一个批次
if cnt > D:#天数过多,增加运载能力
left = mid + 1
else:#天数刚好,或者过少,减少运载能力
right = mid
return left
题目:在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n 个空的篮子,第 i 个篮子的位置在 position[i] ,Morty 想把 m 个球放到这些篮子里,使得任意两球间 最小磁力 最大。已知两个球如果分别位于 x 和 y ,那么它们之间的磁力为 |x - y| 。给你一个整数数组 position 和一个整数 m ,请你返回最大化的最小磁力。
在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n 个空的篮子,第 i 个篮子的位置在 position[i] ,Morty 想把 m 个球放到这些篮子里,使得任意两球间 最小磁力 最大。
已知两个球如果分别位于 x 和 y ,那么它们之间的磁力为 |x - y| 。
给你一个整数数组 position 和一个整数 m ,请你返回最大化的最小磁力。
class Solution:
def maxDistance(self, position: List[int], m: int) -> int:
position.sort()
n = len(position)
left = min([position[i+1]-position[i] for i in range(n-1)])#取得间隔磁力的最大值,最小值
right = position[-1]-position[0]
def check(dis):
i, j = 0, 0
count = 0
while j < n:#从左到右计算两两之间的长度间隔,如果有大于等于所取的dis, 并且个数为大于等于m-1个
while j < n and position[j]-position[i]<dis:#
j += 1
if j < n:
count += 1
i = j
return count >= m-1#因为m是篮球的个数,所以组成的区间间隔有m-1个
while left <= right:#二分法找到合适的值,注意这里是闭区间
dis = left + (right-left)//2
if check(dis):
left = dis+1
else:
right = dis-1
return left-1