【算法】JS + 二分 力扣周赛6355. 统计公平数对的数目

题目链接

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 <= 105

nums.length == n

-109 <= nums[i] <= 109

-109 <= lower <= upper <= 109

题解

lower <= nums[i] + nums[j] <= upper可以转化为找

lower - nums[j] <= nums[i] <= upper - nums[j]

因此我们可以先对nums排序,再加两次二分查找左边界lower - nums[j]和右边界upper - nums[j] + 1

需要理解的是为什么upper - nums[j] + 1,这个+1是什么原因

首先,二分查找的target,含义是指第一个大于等于target的数的索引,

举个例子从[1,3,5,7,9,9]中找8到9之间的数

我们的思路则应该是找到第一个大于等8的数的索引,以及最后一个小于等于9的数索引

找最后一个小于等于9的数字的索引,可以等价为找第一个大于等于9+1的数的索引index值-1

/**
 * @param {number[]} nums
 * @param {number} lower
 * @param {number} upper
 * @return {number}
 */
var countFairPairs = function(nums, lower, upper) {
    // 1 2 5 7 9   11 11
    nums.sort((x, y) => x - y)
    let ans = 0
    nums.forEach((num, index) => {
        const l = bs(nums, index + 1, nums.length - 1, lower - num)
        const r = bs(nums, index + 1, nums.length - 1, upper - num + 1)
        ans += r - l
    })
    return ans

    // 二分查找第一个 >= target的数的下标
    function bs(nums, left, right, target) {
        while (left <= right) {
            const mid = ((right - left) >> 1) + left // 防止left + right溢出
            if (nums[mid] < target) {
                left = mid + 1 // [mid + 1, right]
            } else {
                right = mid - 1 // [left, mid - 1]
            }
        }
        // right + 1 或者 left 为 “第一个 >= target的数的下标”
        return left
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值