leetcode
leetcode 2251 ~ 2300
统计公平数对的数目

统计公平数对的数目

难度:

标签:

题目描述

Given a 0-indexed integer array nums of size n and two integers lower and upper, return the number of fair pairs.

A pair (i, j) is fair if:

  • 0 <= i < j < n, and
  • lower <= nums[i] + nums[j] <= upper

 

Example 1:

Input: nums = [0,1,7,4,4,5], lower = 3, upper = 6
Output: 6
Explanation: There are 6 fair pairs: (0,3), (0,4), (0,5), (1,3), (1,4), and (1,5).

Example 2:

Input: nums = [1,7,9,2,5], lower = 11, upper = 11
Output: 1
Explanation: There is a single fair pair: (2,3).

 

Constraints:

  • 1 <= nums.length <= 105
  • nums.length == n
  • -109 <= nums[i] <= 109
  • -109 <= lower <= upper <= 109

代码结果

运行时间: 163 ms, 内存: 29.2 MB


/*
 * 思路:
 * 1. 使用流来简化代码。
 * 2. 通过两次迭代生成所有可能的数对。
 * 3. 过滤出和在 lower 和 upper 之间的数对。
 * 4. 计数公平数对的数量。
 */

import java.util.stream.IntStream;

public class FairPairsStream {
    public int countFairPairs(int[] nums, int lower, int upper) {
        return (int) IntStream.range(0, nums.length)
                .flatMap(i -> IntStream.range(i + 1, nums.length)
                        .filter(j -> {
                            int sum = nums[i] + nums[j];
                            return sum >= lower && sum <= upper;
                        })
                )
                .count();
    }
}

解释

方法:

这个题解采用了排序加双指针的方法。首先对数组进行排序,然后使用一个辅助函数fn来计算在某个目标值target下,数组中有多少对数的和小于等于target。在fn中,使用双指针left和right分别指向数组的开头和末尾,当nums[left] + nums[right]大于target时,说明需要减小和,所以将right指针左移;否则,说明找到了right - left对数的和小于等于target,将这些对数加入到计数中,并将left指针右移。最后,返回公平数对的数目,即在upper下的数对数目减去在lower-1下的数对数目。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在计算公平数对时选择使用排序加双指针的方法?这种方法与其他可能的解法相比有何优势或特点?
排序加双指针方法在处理数组中的两数问题时非常有效,尤其是当问题涉及到数对和的比较时。通过排序,我们可以将复杂度较高的问题简化,因为排序后的数组可以让我们更容易地通过调整指针来控制数对的和。具体到这个问题中,使用双指针可以有效地搜索所有可能的数对组合,并快速判断它们的和是否符合条件。相比于暴力方法(O(n^2)复杂度),排序加双指针方法将时间复杂度降低到O(n log n)(排序)+ O(n)(双指针遍历),从而实现更高效的计算。此外,这种方法在实现上直观且容易理解,有助于避免复杂的哈希表或二分搜索实现,尤其是在边界条件处理上更为简洁。
🦆
在使用双指针算法时,如何保证所有可能的符合条件的数对都被正确统计,而没有遗漏或重复计算?
在双指针算法中,通过逐步移动左指针(left)或右指针(right)来遍历所有可能的数对。当`nums[left] + nums[right]`超过目标值时,减小`right`(即减小数对的和)以搜索更小的和;当和小于等于目标值时,则所有从`left`到`right`之间的点都可以与`left`点组成合法的数对(因为数组是排序的,所以这些数对的和也一定小于等于目标值)。这时,直接计算这些数对的数量(`right - left`),然后移动`left`指针继续寻找新的可能数对。这种方法确保了每个数对只被计算一次,且没有遗漏。
🦆
函数`fn`在计算小于等于目标值的数对时,为什么在`nums[left] + nums[right] <= target`的情况下可以直接增加`right - left`个计数?这背后的逻辑是什么?
这种计算方法的逻辑基于数组已经被排序的事实。当`nums[left] + nums[right]`小于等于目标值时,由于数组是升序的,因此`nums[left]`和`left`与`right`之间的任何一个元素相加也都必然小于等于目标值。所以,所有这些元素都可以与`left`形成一个有效的数对。`right - left`即为这些满足条件的数对的数量,因为它表示`left`与其右侧所有元素(不包括`right`本身)之间的距离。每次这样计算后,将`left`右移一位,是为了寻找下一个可能的组合,确保每个可能的数对都能被检查到。

相关问题