本篇基于b站灵茶山艾府。
34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums
,和一个目标值 target
。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target
,返回 [-1, -1]
。
你必须设计并实现时间复杂度为 O(log n)
的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
class Solution:
def lower_bound(self, nums, target): # 左闭右闭区间
left = 0
right = len(nums) - 1
# 区间内表示未分类
while left <= right: # 区间不为空
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1 # [mid+1,right]
else:
right = mid - 1 # [left,mid-1]
# left-1都是表示小于target的
# right+1都是表示大于等于target的
return left # 返回的是大于等于target的值
def lower_bound2(self, nums, target): # 左闭右开区间
left = 0
right = len(nums)
# 区间内表示未分类
while left != right: # 区间不为空
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1 # [mid+1,right]
else:
right = mid # [left,mid)
# left-1都是表示小于target的
# right都是表示大于等于target的
return left # right
def lower_bound3(self, nums, target): # 左开右开区间
left = -1
right = len(nums)
# 区间内表示未分类
while left + 1 != right: # 区间不为空
mid = (left + right) // 2
if nums[mid] < target:
left = mid # (mid,right)
else:
right = mid # (left,mid)
# left都是表示小于target的
# right都是表示大于等于target的
return right
def searchRange(self, nums: List[int], target: int) -> List[int]:
start = self.lower_bound(nums, target)
if start == len(nums) or nums[start] != target:
return [-1, -1]
end = self.lower_bound(nums, target + 1) - 1
return [start, end]
'''
总结
# 求大于x的第一个下标,就相当于求大于等于x+1的第一个下标
# 求小于x的最后一个下标,就相当于求大于等于x的第一个下标再-1
# 求小于等于x的最后一个下标,就相当于求大于等于x+1的第一个下标再-1
'''
2529. 正整数和负整数的最大计数
给你一个按 非递减顺序 排列的数组 nums
,返回正整数数目和负整数数目中的最大值。
- 换句话讲,如果
nums
中正整数的数目是pos
,而负整数的数目是neg
,返回pos
和neg
二者中的最大值。
注意:0
既不是正整数也不是负整数。
示例 1:
输入:nums = [-2,-1,-1,1,2,3]
输出:3
解释:共有 3 个正整数和 3 个负整数。计数得到的最大值是 3 。
示例 2:
输入:nums = [-3,-2,-1,0,0,1,2]
输出:3
解释:共有 2 个正整数和 3 个负整数。计数得到的最大值是 3 。
示例 3:
输入:nums = [5,20,66,1314]
输出:4
解释:共有 4 个正整数和 0 个负整数。计数得到的最大值是 4 。
class Solution:
def maximumCount(self, nums: List[int]) -> int:
# 找大于等于1的第一个下标
# 找小于等于-1的第一个下标,就是找大于等于0的第一个下标-1
def binary_search(nums, target):
left = -1
right = len(nums)
while left + 1 != right: # (left,right)
mid = (left + right) // 2
if nums[mid] < target:
left = mid
else:
right = mid
return right
index1 = binary_search(nums, 1)
pos = len(nums) - index1
index2 = binary_search(nums, 0) - 1
neg = index2 + 1
return max(pos, neg)
2300. 咒语和药水的成功对数
给你两个正整数数组 spells
和 potions
,长度分别为 n
和 m
,其中 spells[i]
表示第 i
个咒语的能量强度,potions[j]
表示第 j
瓶药水的能量强度。
同时给你一个整数 success
。一个咒语和药水的能量强度 相乘 如果 大于等于 success
,那么它们视为一对 成功 的组合。
请你返回一个长度为 n
的整数数组 pairs
,其中 pairs[i]
是能跟第 i
个咒语成功组合的 药水 数目。
示例 1:
输入:spells = [5,1,3], potions = [1,2,3,4,5], success = 7
输出:[4,0,3]
解释:
- 第 0 个咒语:5 * [1,2,3,4,5] = [5,10,15,20,25] 。总共 4 个成功组合。
- 第 1 个咒语:1 * [1,2,3,4,5] = [1,2,3,4,5] 。总共 0 个成功组合。
- 第 2 个咒语:3 * [1,2,3,4,5] = [3,6,9,12,15] 。总共 3 个成功组合。
所以返回 [4,0,3] 。
示例 2:
输入:spells = [3,1,2], potions = [8,5,8], success = 16
输出:[2,0,2]
解释:
- 第 0 个咒语:3 * [8,5,8] = [24,15,24] 。总共 2 个成功组合。
- 第 1 个咒语:1 * [8,5,8] = [8,5,8] 。总共 0 个成功组合。
- 第 2 个咒语:2 * [8,5,8] = [16,10,16] 。总共 2 个成功组合。
所以返回 [2,0,2] 。
class Solution:
def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]:
potions.sort()
def binary_search(nums, target, spell):
left = -1
right = len(nums)
while left + 1 != right:
mid = (left + right) // 2
if nums[mid] * spell < target:
left = mid
else:
right = mid
return right
ans = []
for spell in spells:
# 找出大于等于success*spell的第一个下标
index = binary_search(potions, success, spell)
ans.append(len(potions) - index)
return ans
2563. 统计公平数对的数目
给你一个下标从 0 开始、长度为 n
的整数数组 nums
,和两个整数 lower
和 upper
,返回 公平数对的数目 。
如果 (i, j)
数对满足以下情况,则认为它是一个 公平数对 :
0 <= i < j < n
,且lower <= nums[i] + nums[j] <= upper
示例 1:
输入:nums = [0,1,7,4,4,5], lower = 3, upper = 6
输出:6
解释:共计 6 个公平数对:(0,3)、(0,4)、(0,5)、(1,3)、(1,4) 和 (1,5) 。
示例 2:
输入:nums = [1,7,9,2,5], lower = 11, upper = 11
输出:1
解释:只有单个公平数对:(2,3) 。
class Solution:
def countFairPairs(self, nums: List[int], lower: int, upper: int) -> int:
nums.sort()
def binary_search(nums, target, l, r):
left = l
right = r
while left + 1 != right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid
else:
right = mid
return right
ans = 0
for i in range(len(nums)):
l = lower - nums[i]
u = upper - nums[i]
# 找出大于等于l的第一个下标
start = binary_search(nums, l, i, len(nums))
# 找出小于等于u的最后一个下标,就是大于等于u+1的第一个下标-1
if 0 <= start < len(nums):
end = binary_search(nums, u + 1, i, len(nums)) - 1
ans += end - start + 1
return ans