2563.统计公平数对的数目:排序 + 二分查找

【LetMeFly】2563.统计公平数对的数目:排序 + 二分查找

力扣题目链接:https://leetcode.cn/problems/count-the-number-of-fair-pairs/

给你一个下标从 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 <= 105
  • nums.length == n
  • -109 <= nums[i] <= 109
  • -109 <= lower <= upper <= 109

解题方法:排序 + 二分查找

要找的是值在一定范围内的$nums[i] + nums[j]$,且加法满足交换律($a+b=b+a$),所以查找结果和元素顺序无关。

所以只需要遍历$nums$的下标作为$i$,并在$i+1$到数组末尾的范围内查找$j$的范围,最终累加到答案中即可。

如何确定$j$的范围?$upper_bound(upper - i) - lower_bound(lower - i)$或$lower_bound(upper - i + 1) - lower_bound(lower - i)$均可。

其中$lower_bound(t)$是非递减数组中第一个插入$t$后数组仍非递减的下标,$upper_bound(t)$是非递减数组中最后一个插入$t$后数组仍非递减的下标。

  • 时间复杂度$O(n\log n)$,其中$n=len(nums)$
  • 空间复杂度$O(\log n)$

AC代码

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* @Author: LetMeFly
* @Date: 2025-04-19 15:51:42
* @LastEditors: LetMeFly.xyz
* @LastEditTime: 2025-04-19 16:12:44
*/
/*
l: first j 满足 nums[j] + nums[i] >= lower | nums[j] >= lower - nums[i]
r: last j 满足 nums[j] + nums[i] <= upper | nums[j] <= upper - nums[i]

l: lower_bound(lower - nums[i])
r: upper_bound(upper - nums[i])
*/
typedef long long ll;
class Solution {
public:
long long countFairPairs(vector<int>& nums, int lower, int upper) {
sort(nums.begin(), nums.end());
ll ans = 0;
for (int i = 0; i < nums.size(); i++) {
ans += upper_bound(nums.begin() + i + 1, nums.end(), upper - nums[i]) - lower_bound(nums.begin() + i + 1, nums.end(), lower - nums[i]);
// cout << i << ": " << i << "[" << lower_bound(nums.begin() + i + 1, nums.end(), lower - nums[i]) - nums.begin() << ", " << upper_bound(nums.begin() + i + 1, nums.end(), upper - nums[i]) - nums.begin() << ')' << endl;
}
return ans;
}
};

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'''
Author: LetMeFly
Date: 2025-04-19 16:13:37
LastEditors: LetMeFly.xyz
LastEditTime: 2025-04-19 16:23:38
'''
from typing import List
from bisect import bisect_left, bisect_right


class Solution:
def countFairPairs(self, nums: List[int], lower: int, upper: int) -> int:
nums.sort()
return sum(bisect_right(nums, upper - nums[i], i + 1) - bisect_left(nums, lower - nums[i], i + 1) for i in range(len(nums)))

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
* @Author: LetMeFly
* @Date: 2025-04-19 16:24:08
* @LastEditors: LetMeFly.xyz
* @LastEditTime: 2025-04-19 16:37:36
*/
import java.util.Arrays;

class Solution {
private int search(int[] nums, int x, int l) { // search [l, len(nums)) 范围内第一个大于等于x的下标
int r = nums.length;
while (l < r) {
int mid = (l + r) >> 1;
if (nums[mid] >= x) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
public long countFairPairs(int[] nums, int lower, int upper) {
Arrays.sort(nums);
long ans = 0;
for (int i = 0; i < nums.length; i++) {
ans += search(nums, upper - nums[i] + 1, i + 1) - search(nums, lower - nums[i], i + 1);
}
return ans;
}
}

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
* @Author: LetMeFly
* @Date: 2025-04-19 16:24:24
* @LastEditors: LetMeFly.xyz
* @LastEditTime: 2025-04-19 16:43:06
*/
package main
import (
"sort"
)

func countFairPairs(nums []int, lower int, upper int) (ans int64) {
sort.Ints(nums)
for i, v := range nums {
l := sort.Search(len(nums), func(x int) bool {return x > i && nums[x] >= lower - v})
r := sort.Search(len(nums), func(x int) bool {return x > i && nums[x] >= upper - v + 1})
ans += int64(r - l)
}
return
}

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源


2563.统计公平数对的数目:排序 + 二分查找
https://blog.letmefly.xyz/2025/04/19/LeetCode 2563.统计公平数对的数目/
作者
发布于
2025年4月19日
许可协议