力扣每日一题0825-658. 找到 K 个最接近的元素

112 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

给定一个 排序好 的数组 arr ,两个整数 kx ,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。

整数 a 比整数 b 更接近 x 需要满足:

  • |a - x| < |b - x| 或者
  • |a - x| == |b - x|a < b

示例 1:

输入:arr = [1,2,3,4,5], k = 4, x = 3
输出:[1,2,3,4]

示例 2:

输入:arr = [1,2,3,4,5], k = 4, x = -1
输出:[1,2,3,4]

排序

首先将数组 arr\textit{arr} 按照「更接近」的定义进行排序,如果 aabb 更接近 xx,那么 aa 将排在 bb 前面。排序完成之后,kk 个最接近的元素就是数组 arr\textit{arr} 的前 kk 个元素,将这 kk 个元素从小到大进行排序后,直接返回。

var findClosestElements = function(arr, k, x) {
    const list = [...arr];
    list.sort((a, b) => {
        if (Math.abs(a - x) !== Math.abs(b - x)) {
            return Math.abs(a - x) - Math.abs(b - x);
        } else {
            return a - b;
        }
    });
    const ans = list.slice(0, k);
    ans.sort((a, b) => a - b);
    return ans;
};

二分查找 + 双指针

假设数组长度为 nn,注意到数组 arr\textit{arr} 已经按照升序排序,我们可以将数组 arr\textit{arr} 分成两部分,前一部分所有元素 [0,left][0, \textit{left}] 都小于 xx,后一部分所有元素 [[right,n1][\textit{right}, n - 1] 都大于等于 xxleft\textit{left}right\textit{right} 都可以通过二分查找获得。

leftleftright\textit{right} 指向的元素都是各自部分最接近 xx 的元素,因此我们可以通过比较 left\textit{left}right\textit{right} 指向的元素获取整体最接近 xx 的元素。如果 xarr[left]arr[right]xx - \textit{arr}[\textit{left}] \le \textit{arr}[\textit{right}] - x,那么将 left\textit{left} 减一,否则将 right\textit{right} 加一。相应地,如果 left\textit{left}right\textit{right} 已经越界,那么不考虑对应部分的元素。

最后,区间 [left+1,right1][\textit{left} + 1, \textit{right} - 1] 的元素就是我们所要获得的结果,返回答案既可。

var findClosestElements = function(arr, k, x) {
    let right = binarySearch(arr, x);
    let left = right - 1;
    while (k-- > 0) {
        if (left < 0) {
            right++;
        } else if (right >= arr.length) {
            left--;
        } else if (x - arr[left] <= arr[right] - x) {
            left--;
        } else {
            right++;
        }
    }
    const ans = [];
    for (let i = left + 1; i < right; i++) {
        ans.push(arr[i]);
    }
    return ans;
}

const binarySearch = (arr, x) => {
    let low = 0, high = arr.length - 1;
    while (low < high) {
        const mid = low + Math.floor((high - low) / 2);
        if (arr[mid] >= x) {
            high = mid;
        } else {
            low = mid + 1;
        }
    }
    return low;
}