开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情
6355. 统计公平数对的数目
给你一个下标从 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) 。
提示:
1 <= nums.length <= 105nums.length == n-109 <= nums[i] <= 109-109 <= lower <= upper <= 109
思路
题目理解起来很容易,只需要依次找到符合题目要求的数对即可,但很显然并没有那么简单,数据量告诉我们,两层遍历会超时,故这道题目考我们的是对于时间复杂度的优化。
仔细思考可以发现,其实数组的顺序对于结果而言是没有任何影响的,我们就可以先得到有序数组,数组有序之后,我们就可以一次得到一个范围内的数对,继而转换为求符合条件的边界值。
对于边界值来说,我们可以通过二分查找来解决,根据下界找数组的左边界,根据上界找数组的右边界,若左边界小于右边界,则可将该范围内的所有数对加入答案。
题解
class Solution {
public long countFairPairs(int[] nums, int lower, int upper) {
long ans = 0;
int n = nums.length;
Arrays.sort(nums);
for(int i = 0; i < n; i++) {
int l = getLower(nums, i + 1, lower - nums[i]);
int r = getUpper(nums, i + 1, upper - nums[i]);
ans += Math.max(0, r - l + 1);
}
return ans;
}
private int getLower(int[] nums, int left, int target) {
int right = nums.length - 1;
while(left <= right) {
int mid = left + ((right - left) >> 1);
if(nums[mid] < target) {
left = mid + 1;
}else {
right = mid - 1;
}
}
return left;
}
private int getUpper(int[] nums, int left, int target) {
int right = nums.length - 1;
while(left <= right) {
int mid = left + ((right - left) >> 1);
if(nums[mid] > target) {
right = mid - 1;
}else {
left = mid + 1;
}
}
return right;
}
}
如果你有其他的思路或者更好的解法,亦或者你发现了文章出现了错误或有不足,欢迎在评论区和我交流,我看到了一定会回复。
写文章不易,如果你觉得文章对你有帮助,麻烦点一下点赞、收藏,你的支持是我写文章的最大动力!