每日一题(5)
统计公平数对的数目
第一个暴力遍历但是会超时
class Solution {
public:long long countFairPairs(vector<int>& nums, int lower, int upper) {int n=nums.size();long long count=0;for(int i=0;i<n-1;++i){for(int j=i+1;j<n;++j){if( nums[i] + nums[j]>=lower&& nums[i] + nums[j]<=upper)count++;}}return count;}
};
这个就是直接根据条件直接进行判断但可能因为复杂度过高导致不会通过
第二个是二分查找法
由于是lower <= nums[i] + nums[j] <= upper可以给两边同时减去nums[j]后就变成了lower-nums[j]<=nums[i]<=upper-nums[j];这样的话只需要找到这俩个数中间的元素就可以了省去了麻烦可以用库函数进行,upper_bound
返回指向第一个大于 upper - nums[j]
的元素的迭代器,即所有小于等于 upper - nums[j]
的元素的范围的结束位置lower_bound
返回指向第一个不小于 lower - nums[j]
的元素的迭代器,即满足条件的起始位置
class Solution {
public:long long countFairPairs(vector<int>& nums, int lower, int upper) {sort(nums.begin(),nums.end());int n=nums.size();long long count=0;for(int j=0;j<n;++j){auto l=upper_bound(nums.begin(),nums.begin()+j,upper-nums[j]);auto r=lower_bound(nums.begin(),nums.begin()+j,lower-nums[j]);count+=l-r;}return count;}
};
为什么排序不影响结果?数对 (i, j)
是无序的,即 (i, j)
和 (j, i)
被认为是同一个数对题目要求 i < j
,这意味着我们只关心数对的组合,不关心原始顺序每个元素的值保持不变,只是位置发生了变化,数对 (nums[i],nums[j])
的和 nums[i] + nums[j]
保持不变,满足条件的数对组合没有改变,只是它们的索引位置发生了变化
方法三用三指针
class Solution {
public:long long countFairPairs(vector<int>& nums, int lower, int upper) {ranges::sort(nums);long long ans = 0;int l = nums.size(), r = l;for (int j = 0; j < nums.size(); j++) {while (r && nums[r - 1] > upper - nums[j]) {r--;}while (l && nums[l - 1] >= lower - nums[j]) {l--;}ans += min(r, j) - min(l, j);}return ans;}
};
方法是差不多的和第二个只是变成了指针
为什么需要 min(r, j) 和 min(l, j)?
这些指针是基于整个排序后数组计算的,没有考虑 i < j
的限制