给你一个下标从 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,9) 。
解法:二分
class Solution {
public long countFairPairs(int[] nums, int lower, int upper) {
Arrays.sort(nums);
// nums[i] 范围:[lower - nums[j] , upper - nums[j]]
// 枚举i,找到满足(nums[j] <= upper - nums[i] 的j的个数) - (nums[j] < lower - nums[i] 的j的个数)
long ans = 0;
// 0 <= i < j < n 即 0 <= i < n - 1, i < j < n
for (int i = 0; i < nums.length - 1; i++) {
// i < j,枚举i,找满足要求的j的个数,二分查找i + 1为左边界
// nums[lowerBound] >= lower - nums[i]
int lowerBound = getLowerBound(nums, i, lower);
// nums[upperBound] <= upper - nums[i]
int upperBound = getUpperBound(nums, i, upper);
ans += upperBound - lowerBound + 1;
}
return ans;
}
private int getUpperBound(int[] nums, int i, int goal) {
int left = i + 1;
int right = nums.length - 1;
while (left <= right) {
// 循环不变量:
// nums[right + 1] > goal - nums[i]
// nums[left - 1] <= goal - nums[i]
int mid = left + (right - left) / 2;
if (nums[mid] > goal - nums[i]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return right;
}
private int getLowerBound(int[] nums, int i, int goal) {
int left = i + 1;
int right = nums.length - 1;
while (left <= right) {
// 循环不变量:
// nums[right + 1] >= goal - nums[i]
// nums[left - 1] < goal - nums[i]
int mid = left + (right - left) / 2;
if (nums[mid] < goal - nums[i]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
}
复杂度分析
- 时间复杂度:O(nlogn),其中 n 为 nums 的长度。
- 空间复杂度:O(1)。忽略排序的栈开销,仅用到若干额外变量。
相似题型: