模板
1、二分查找(数组单调递增)
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
return -1
2、bisect_right
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid
return left
3、bisect_left
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid
return left
注: 1、由于整除一般向下取整,所以left = mid + 1防止死循环
2、根据1的性质,当查找的数介于两数之间时,都取较大者
3、当查找的数在数组中存在多个时,bisect_left取第一个数的位置,bisect_right取最后一个数的下一个位置。
4、待查找的数不小于数组的最大值时,bisect_right返回len(nums),待查找的数大于数组最大值时,bisect_left返回len(nums)
例题:
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
left, right = bisect_left(nums, target), bisect_right(nums, target)
if left == right: return -1, -1
return left, right - 1
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
return bisect_left(nums, target)
class Solution:
def longestObstacleCourseAtEachPosition(self, obstacles: List[int]) -> List[int]:
res, arr = [], []
for o in obstacles:
idx = bisect_right(arr, o)
res.append(idx + 1)
if idx >= len(arr):
arr.append(o)
else:
arr[idx] = o
return res
特征查找
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
nums = [-float('inf')] + nums + [-float('inf')]
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid - 1] < nums[mid] > nums[mid + 1]:
return mid - 1
elif nums[mid] < nums[mid + 1]:
left = mid + 1
elif nums[mid] < nums[mid - 1]:
right = mid - 1
return -1
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
if mid % 2 == 1:
mid -= 1
if nums[mid] == nums[mid + 1]:
lo = mid + 2
else:
hi = mid
return nums[lo]
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right) // 2
if 0 < mid < len(nums) - 1:
if nums[mid - 1] > nums[mid] <= nums[mid + 1]:
return nums[mid]
if nums[mid] < nums[right]:
right = mid - 1
elif nums[mid] > nums[right]:
left = mid + 1
else:
right -= 1
return nums[left]
class Solution:
def missingElement(self, nums: List[int], k: int) -> int:
n = len(nums)
l, r = 0, n
while l < r:
mid = (l + r) // 2
if nums[mid] - (nums[0] + mid) >= k:
r = mid
else:
l = mid + 1
return nums[l - 1] + k - (nums[l - 1] - (nums[0] + (l - 1)))
还可以写成如下形式:
class Solution:
def missingElement(self, nums: List[int], k: int) -> int:
self.__class__.__getitem__ = lambda self, mid: nums[mid] - (nums[0] + mid) >= k
l = bisect.bisect_left(self, True, 0, len(nums))
return nums[l - 1] + k - (nums[l - 1] - (nums[0] + (l - 1)))
class Solution:
def findPeakGrid(self, mat: List[List[int]]) -> List[int]:
m, n = len(mat), len(mat[0])
l, r = 0, m - 1
@cache
def getMax(i):
maxl, maxi = 0, 0
for j in range(n):
if mat[i][j] > maxl:
maxl, maxi = mat[i][j], j
return maxl, maxi
def getE(i, j):
if not 0 <= i < m and 0 <= j < n: return -1
return mat[i][j]
while l <= r:
mid = (l + r) // 2
mx, idx = getMax(mid)
if mx > getE(mid - 1, idx) and mx > getE(mid + 1, idx):
return [mid, idx]
elif getE(mid - 1, idx) > getE(mid + 1, idx):
r = mid - 1
else:
l = mid + 1
return -1
class Solution:
def superEggDrop(self, k: int, n: int) -> int:
@cache
def dfs(t, k):
if t == 0 or t == 1 or k == 1: return t
left, right = 1, t
while left + 1 < right:
mid = (left + right) // 2
t1 = dfs(mid - 1, k - 1) + 1
t2 = dfs(t - mid, k) + 1
if t1 < t2:
left = mid
elif t1 > t2:
right = mid
else:
return t1
return 1 + min(max(dfs(x - 1, k - 1), dfs(t - x, k)) for x in (left, right))
return dfs(n, k)
矩阵查找
搜索二维矩阵 IIhttps://leetcode.cn/problems/search-a-2d-matrix-ii/
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
if not matrix or len(matrix) == 0:
return False
m, n = 0, len(matrix[0]) - 1
while m < len(matrix) and n >= 0:
if matrix[m][n] == target:
return True
elif matrix[m][n] > target:
n -= 1
else:
m += 1
return False
二叉搜索树
二叉搜索树中的搜索https://leetcode.cn/problems/search-in-a-binary-search-tree/
class Solution:
def searchBST(self, root: TreeNode, val: int) -> TreeNode:
cur = root
while cur:
if cur.val == val:
return cur
elif cur.val < val:
cur = cur.right
else:
cur = cur.left
return None
二叉搜索树中的中序后继https://leetcode.cn/problems/P5rCT8/
class Solution:
def inorderSuccessor(self, root: 'TreeNode', p: 'TreeNode') -> 'TreeNode':
cur, res = root, None
while cur:
if cur.val > p.val:
res = cur
cur = cur.left
else:
cur = cur.right
return res
完全二叉树的节点个数https://leetcode.cn/problems/count-complete-tree-nodes/
三分查找
找到最大整数的索引https://leetcode.cn/problems/find-the-index-of-the-large-integer/ 均分成三分,不能均分的,保证前两份相等,然后将两个相等数量份的进行比较,如果相等,则在第三份上,反之在前两份上
class Solution:
def getIndex(self, reader: 'ArrayReader') -> int:
l, r = 0, reader.length() - 1
while l < r:
n = (r - l + 1) // 3 + ((r - l + 1) % 3 > 0)
res = reader.compareSub(l, l + n - 1, l + n, l + 2 * n - 1)
if res == 1:
r = l + n - 1
elif res == -1:
l, r = l + n, l + 2 * n - 1
else:
l = l + 2 * n
return l
二分查找猜结果
用途:直接求解比较困难的问题
前提:1、有上下边界
2、能根据结果缩小边界(函数具有单调性)
class Solution:
def minEatingSpeed(self, piles: List[int], h: int) -> int:
left, right = ceil(sum(piles) / h), max(piles)
while left < right:
mid = left + (right - left) // 2
H = 0
for p in piles:
H += ceil(p / mid)
if H > h:
left = mid + 1
else:
right = mid
return left
class Solution:
def longestRepeatingSubstring(self, s: str) -> int:
@lru_cache(None)
def check(k):
pre = set()
for i in range(n - k + 1):
cur = s[i: i + k]
if cur in pre:
return True
pre.add(cur)
return False
n = len(s)
low, high = 1, n
while low < high:
mid = low + (high - low) // 2
if check(mid):
low = mid + 1
else:
high = mid
return high - 1
类似题:最长重复子数组
class Solution:
def matrixMedian(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
def check(x):
cnt = 0
for row in grid:
cnt += bisect_right(row, x)
return cnt <= n * m // 2
l, r = min(row[0] for row in grid), max(row[-1] for row in grid)
while l < r:
mid = (l + r) // 2
if check(mid):
l = mid + 1
else:
r = mid
return l
类似题:有序矩阵中第 K 小的元素
- 二分查找与滑动窗口结合 -- 第 K 小的子数组和·
class Solution:
def kthSmallestSubarraySum(self, nums: List[int], k: int) -> int:
def getK(x):
res = 0
j, s = 0, 0
for i in range(len(nums)):
s += nums[i]
while s > x:
s -= nums[j]
j += 1
res += i - j + 1
return res
l, r = min(nums), sum(nums)
while l < r:
mid = (l + r) // 2
if getK(mid) < k:
l = mid + 1
else:
r = mid
return l
- 浮点数二分查找 -- 通过倒水操作让所有的水桶所含水量相等
对于每次二分的值 x,水量对于该值的木桶需要往外倒水,水量小于该值的木桶应该被倒水,往外倒水的水量之和记为 a,需要水量之和记为 b (注意不能忘记损失比例),只要满足 a⩾b 则 x 可以实现,因为多余的水量可以互相倒水,每次倒水存在损失比例,故而可以逐渐逼近 x。
class Solution:
def equalizeWater(self, buckets: List[int], loss: int) -> float:
n, l, r, s = len(buckets), 0, max(buckets), sum(buckets)
def helper(x):
s1 = s2 = 0
for b in buckets:
if b > x:
# 倒掉了多少水
s1 += b - x
else:
# 至少需要多少水
s2 += (x - b) * 100 / (100 - loss)
return s1 >= s2
while (r - l) > 1e-5:
mid = (l + r) / 2
if helper(mid):
l = mid
else:
r = mid - 0.000001
return l
class Solution:
def minimizeSet(self, d1: int, d2: int, uniqueCnt1: int, uniqueCnt2: int) -> int:
lcm = math.lcm(d1, d2)
def check(m: int) -> bool:
return m - m // d1 >= uniqueCnt1 \
and m - m // d2 >= uniqueCnt2 \
and m - m // lcm >= uniqueCnt1 + uniqueCnt2
return bisect_left(range((uniqueCnt1 + uniqueCnt2) * 2 - 1), True, key=check)
注意,只有被二分的函数单调时,才可以使用,否则,例如:根据限制分割消息,则导致结果错误。
有序数组二分查找
-
最近的房间 (有序数组 + 双指针)
class Solution:
def closestRoom(self, rooms: List[List[int]], queries: List[List[int]]) -> List[int]:
from sortedcontainers import SortedList
rooms.sort(key=lambda x:-x[1])
res = [0] * len(queries)
arr, j = SortedList(), 0
for i, (p, s) in sorted([(i, q) for i, q in enumerate(queries)], key=lambda x:-x[1][1]):
while j < len(rooms) and rooms[j][1] >= s:
arr.add(rooms[j][0])
j += 1
idx = arr.bisect_right(p)
r, d = -1, inf
if idx > 0:
if abs(arr[idx - 1] - p) < d:
r, d = arr[idx - 1], abs(arr[idx - 1] - p)
if idx < len(arr):
if abs(arr[idx] - p) < d:
r, d = arr[idx], abs(arr[idx] - p)
res[i] = r
return res
折半查找
class Solution:
def splitArraySameAverage(self, nums: List[int]) -> bool:
n = len(nums)
m = defaultdict(set)
def dfs1(i, c, s):
if i == n // 2:
m[c].add(s)
return
dfs1(i + 1, c + 1, s + nums[i])
dfs1(i + 1, c, s)
ss = sum(nums)
dfs1(0, 0, 0)
def dfs2(i, c, s):
if i == n:
for c2 in m:
if not 0 < (c + c2) < n: continue
a1 = (c + c2) * ss / n - s
a2 = (c + c2) * ss // n - s
if abs(a1 - a2) < 1e-6 and a2 in m[c2]:
return True
else:
return False
if dfs2(i + 1, c + 1, s + nums[i]): return True
if dfs2(i + 1, c, s): return True
return False
return dfs2(n // 2, 0, 0)
class Solution:
def minimumDifference(self, nums: List[int]) -> int:
n = len(nums)
from sortedcontainers import SortedList
m = defaultdict(SortedList)
def dfs1(i, c, s):
if i == n // 2:
m[c].add(s)
return
dfs1(i + 1, c + 1, s + nums[i])
dfs1(i + 1, c, s)
ss = sum(nums)
dfs1(0, 0, 0)
minl = inf
def dfs2(i, c, s):
if i == n:
c2 = n // 2 - c
nonlocal minl
idx = m[c2].bisect_right((ss - 2* s) / 2)
if idx < len(m[c2]):
s1 = m[c2][idx]
left = s1 + s
right = ss - s1 - s
minl = min(minl, abs(left - right))
if idx - 1 >= 0:
s1 = m[c2][idx - 1]
left = s1 + s
right = ss - s1 - s
minl = min(minl, abs(left - right))
return
dfs2(i + 1, c + 1, s + nums[i])
dfs2(i + 1, c, s)
dfs2(n // 2, 0, 0)
return minl