题目来源:LC1300
这道题目是一道比较经典的二分查找题。
我们注意到,当value越大时,数组之和越大,当value越小时,数组之和越小。因此,我们可以利用数组之和是value的单调递增函数这个性质来进行二分查找。
最大的可能value值是max(arr),因为在那之后继续增加value并不会改变数组里的元素值,从而无法改变数组之和。
最小的可能value值是target/n-1,这里n是数组的长度。value继续减小只会让数组之和与target渐行渐远。
因此,我们只需在这两个值之间进行二分查找即可。
def findBestValue(self, arr: List[int], target: int) -> int: # 计算value为x时,数组之和 def getsum(x): res = 0 for val in arr: res = res + min(val, x) return res # 初始化 n = len(arr) l = target // n - 1 r = max(arr) # 二分查找 while l < r - 1: mid = (l + r)//2 sum_arr = getsum(mid) if sum_arr == target: return mid if sum_arr < target: l = mid else: r = mid if abs(getsum(l) - target) > abs(getsum(r) - target): return r return l
复杂度分析:
- 时间上:外层二分查找最多需要log(max(arr))次循环,内层getsum函数需要最多n次循环。由于max(arr)最大不会超过10^5,因此 log(max(arr)) < 17 可以看做是一个常数。时间复杂度为O(n)。
- 空间上:除去输入数组,我们只用了常数个变量,因此空间复杂度为O(1)。