|
| 1 | +--- |
| 2 | +title: 2563.统计公平数对的数目:排序 + 二分查找 |
| 3 | +date: 2025-04-19 16:46:20 |
| 4 | +tags: [题解, LeetCode, 中等, 数组, 双指针, 二分查找, 排序] |
| 5 | +categories: [题解, LeetCode] |
| 6 | +--- |
| 7 | + |
| 8 | +# 【LetMeFly】2563.统计公平数对的数目:排序 + 二分查找 |
| 9 | + |
| 10 | +力扣题目链接:[https://leetcode.cn/problems/count-the-number-of-fair-pairs/](https://leetcode.cn/problems/count-the-number-of-fair-pairs/) |
| 11 | + |
| 12 | +<p>给你一个下标从 <strong>0</strong> 开始、长度为 <code>n</code> 的整数数组 <code>nums</code> ,和两个整数 <code>lower</code> 和 <code>upper</code> ,返回 <strong>公平数对的数目</strong> 。</p> |
| 13 | + |
| 14 | +<p>如果 <code>(i, j)</code> 数对满足以下情况,则认为它是一个 <strong>公平数对</strong> :</p> |
| 15 | + |
| 16 | +<ul> |
| 17 | + <li><code>0 <= i < j < n</code>,且</li> |
| 18 | + <li><code>lower <= nums[i] + nums[j] <= upper</code></li> |
| 19 | +</ul> |
| 20 | + |
| 21 | +<p> </p> |
| 22 | + |
| 23 | +<p><b>示例 1:</b></p> |
| 24 | + |
| 25 | +<pre> |
| 26 | +<b>输入:</b>nums = [0,1,7,4,4,5], lower = 3, upper = 6 |
| 27 | +<b>输出:</b>6 |
| 28 | +<b>解释:</b>共计 6 个公平数对:(0,3)、(0,4)、(0,5)、(1,3)、(1,4) 和 (1,5) 。 |
| 29 | +</pre> |
| 30 | + |
| 31 | +<p><b>示例 2:</b></p> |
| 32 | + |
| 33 | +<pre> |
| 34 | +<b>输入:</b>nums = [1,7,9,2,5], lower = 11, upper = 11 |
| 35 | +<b>输出:</b>1 |
| 36 | +<b>解释:</b>只有单个公平数对:(2,3) 。 |
| 37 | +</pre> |
| 38 | + |
| 39 | +<p> </p> |
| 40 | + |
| 41 | +<p><strong>提示:</strong></p> |
| 42 | + |
| 43 | +<ul> |
| 44 | + <li><code>1 <= nums.length <= 10<sup>5</sup></code></li> |
| 45 | + <li><code>nums.length == n</code></li> |
| 46 | + <li><code>-10<sup>9</sup> <= nums[i] <= 10<sup>9</sup></code></li> |
| 47 | + <li><code>-10<sup>9</sup> <= lower <= upper <= 10<sup>9</sup></code></li> |
| 48 | +</ul> |
| 49 | + |
| 50 | + |
| 51 | + |
| 52 | +## 解题方法:排序 + 二分查找 |
| 53 | + |
| 54 | +要找的是值在一定范围内的$nums[i] + nums[j]$,且加法满足交换律($a+b=b+a$),所以查找结果和元素顺序无关。 |
| 55 | + |
| 56 | +所以只需要遍历$nums$的下标作为$i$,并在$i+1$到数组末尾的范围内查找$j$的范围,最终累加到答案中即可。 |
| 57 | + |
| 58 | +> 如何确定$j$的范围?$upper\_bound(upper - i) - lower\_bound(lower - i)$或$lower\_bound(upper - i + 1) - lower_bound(lower - i)$均可。 |
| 59 | +> |
| 60 | +> 其中$lower_bound(t)$是非递减数组中第一个插入$t$后数组仍非递减的下标,$upper_bound(t)$是非递减数组中最后一个插入$t$后数组仍非递减的下标。 |
| 61 | +
|
| 62 | ++ 时间复杂度$O(n\log n)$,其中$n=len(nums)$ |
| 63 | ++ 空间复杂度$O(\log n)$ |
| 64 | + |
| 65 | +### AC代码 |
| 66 | + |
| 67 | +#### C++ |
| 68 | + |
| 69 | +```cpp |
| 70 | +/* |
| 71 | + * @Author: LetMeFly |
| 72 | + * @Date: 2025-04-19 15:51:42 |
| 73 | + * @LastEditors: LetMeFly.xyz |
| 74 | + * @LastEditTime: 2025-04-19 16:12:44 |
| 75 | + */ |
| 76 | +/* |
| 77 | +l: first j 满足 nums[j] + nums[i] >= lower | nums[j] >= lower - nums[i] |
| 78 | +r: last j 满足 nums[j] + nums[i] <= upper | nums[j] <= upper - nums[i] |
| 79 | +
|
| 80 | +l: lower_bound(lower - nums[i]) |
| 81 | +r: upper_bound(upper - nums[i]) |
| 82 | +*/ |
| 83 | +typedef long long ll; |
| 84 | +class Solution { |
| 85 | +public: |
| 86 | + long long countFairPairs(vector<int>& nums, int lower, int upper) { |
| 87 | + sort(nums.begin(), nums.end()); |
| 88 | + ll ans = 0; |
| 89 | + for (int i = 0; i < nums.size(); i++) { |
| 90 | + ans += upper_bound(nums.begin() + i + 1, nums.end(), upper - nums[i]) - lower_bound(nums.begin() + i + 1, nums.end(), lower - nums[i]); |
| 91 | + // 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; |
| 92 | + } |
| 93 | + return ans; |
| 94 | + } |
| 95 | +}; |
| 96 | +``` |
| 97 | +
|
| 98 | +#### Python |
| 99 | +
|
| 100 | +```python |
| 101 | +''' |
| 102 | +Author: LetMeFly |
| 103 | +Date: 2025-04-19 16:13:37 |
| 104 | +LastEditors: LetMeFly.xyz |
| 105 | +LastEditTime: 2025-04-19 16:23:38 |
| 106 | +''' |
| 107 | +from typing import List |
| 108 | +from bisect import bisect_left, bisect_right |
| 109 | +
|
| 110 | +
|
| 111 | +class Solution: |
| 112 | + def countFairPairs(self, nums: List[int], lower: int, upper: int) -> int: |
| 113 | + nums.sort() |
| 114 | + return sum(bisect_right(nums, upper - nums[i], i + 1) - bisect_left(nums, lower - nums[i], i + 1) for i in range(len(nums))) |
| 115 | +``` |
| 116 | + |
| 117 | +#### Java |
| 118 | + |
| 119 | +```java |
| 120 | +/* |
| 121 | + * @Author: LetMeFly |
| 122 | + * @Date: 2025-04-19 16:24:08 |
| 123 | + * @LastEditors: LetMeFly.xyz |
| 124 | + * @LastEditTime: 2025-04-19 16:37:36 |
| 125 | + */ |
| 126 | +import java.util.Arrays; |
| 127 | + |
| 128 | +class Solution { |
| 129 | + private int search(int[] nums, int x, int l) { // search [l, len(nums)) 范围内第一个大于等于x的下标 |
| 130 | + int r = nums.length; |
| 131 | + while (l < r) { |
| 132 | + int mid = (l + r) >> 1; |
| 133 | + if (nums[mid] >= x) { |
| 134 | + r = mid; |
| 135 | + } else { |
| 136 | + l = mid + 1; |
| 137 | + } |
| 138 | + } |
| 139 | + return l; |
| 140 | + } |
| 141 | + public long countFairPairs(int[] nums, int lower, int upper) { |
| 142 | + Arrays.sort(nums); |
| 143 | + long ans = 0; |
| 144 | + for (int i = 0; i < nums.length; i++) { |
| 145 | + ans += search(nums, upper - nums[i] + 1, i + 1) - search(nums, lower - nums[i], i + 1); |
| 146 | + } |
| 147 | + return ans; |
| 148 | + } |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +#### Go |
| 153 | + |
| 154 | +```go |
| 155 | +/* |
| 156 | + * @Author: LetMeFly |
| 157 | + * @Date: 2025-04-19 16:24:24 |
| 158 | + * @LastEditors: LetMeFly.xyz |
| 159 | + * @LastEditTime: 2025-04-19 16:43:06 |
| 160 | + */ |
| 161 | +package main |
| 162 | +import ( |
| 163 | + "sort" |
| 164 | +) |
| 165 | + |
| 166 | +func countFairPairs(nums []int, lower int, upper int) (ans int64) { |
| 167 | + sort.Ints(nums) |
| 168 | + for i, v := range nums { |
| 169 | + l := sort.Search(len(nums), func(x int) bool {return x > i && nums[x] >= lower - v}) |
| 170 | + r := sort.Search(len(nums), func(x int) bool {return x > i && nums[x] >= upper - v + 1}) |
| 171 | + ans += int64(r - l) |
| 172 | + } |
| 173 | + return |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +> 同步发文于[CSDN](https://letmefly.blog.csdn.net/article/details/147354620)和我的[个人博客](https://blog.letmefly.xyz/),原创不易,转载经作者同意后请附上[原文链接](https://blog.letmefly.xyz/2025/04/19/LeetCode%202563.%E7%BB%9F%E8%AE%A1%E5%85%AC%E5%B9%B3%E6%95%B0%E5%AF%B9%E7%9A%84%E6%95%B0%E7%9B%AE/)哦~ |
| 178 | +> |
| 179 | +> 千篇源码题解[已开源](https://github.com/LetMeFly666/LeetCode) |
0 commit comments