力扣每日一题0615-719. 找出第 K 小的数对距离

139 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

数对 (a,b) 由整数 ab 组成,其数对距离定义为 ab 的绝对差值。

给你一个整数数组 nums 和一个整数 k ,数对由 nums[i]nums[j] 组成且满足 0 <= i < j < nums.length 。返回 所有数对距离中 第 k 小的数对距离。

示例 1:

输入:nums = [1,3,1], k = 1
输出:0
解释:数对和对应的距离如下:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
距离第 1 小的数对是 (1,1) ,距离为 0

示例 2:

输入:nums = [1,1,1], k = 2
输出:0

示例 3:

输入:nums = [1,6,1], k = 3
输出:5

排序 + 二分查找

先将数组 nums\textit{nums} 从小到大进行排序。因为第 kk 小的数对距离必然在区间 [0,max(nums)min(nums)][0, \max (\textit{nums}) - \min(\textit{nums})] 内,令 left=0\textit{left} = 0right=max(nums)min(nums)\textit{right} = \max (\textit{nums}) - \min(\textit{nums}),我们在区间 [left,right][\textit{left}, \textit{right}] 上进行二分。

对于当前搜索的距离 mid\textit{mid},计算所有距离小于等于 mid\textit{mid} 的数对数目 cnt\textit{cnt},如果 cntk\textit{cnt} \ge k,那么 right=mid1\textit{right} = \textit{mid} - 1,否则 left=mid+1\textit{left} = \textit{mid} + 1。当 left>right\textit{left} \gt \textit{right} 时,终止搜索,那么第 kk 小的数对距离为 left\textit{left}

给定距离 mid\textit{mid},计算所有距离小于等于 mid\textit{mid} 的数对数目 cnt\textit{cnt} 可以使用二分查找:枚举所有数对的右端点 jj,二分查找大于等于 nums[j]mid\textit{nums}[j] - \textit{mid} 的最小值的下标 ii,那么右端点为 jj 且距离小于等于 mid\textit{mid} 的数对数目为 jij - i,计算这些数目之和。

var smallestDistancePair = function(nums, k) {
    nums.sort((a, b) => a - b);
    let n = nums.length, left = 0, right = nums[n - 1] - nums[0];
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        let cnt = 0;
        for (let j = 0; j < n; j++) {
            const i = binarySearch(nums, j, nums[j] - mid);
            cnt += j - i;
        }
        if (cnt >= k) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return left;
};

const binarySearch = (nums, end, target) => {
    let left = 0, right = end;
    while (left < right) {
        const mid = Math.floor((left + right) / 2);
        if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}