[路飞]_658. 找到 K 个最接近的元素

1,216 阅读1分钟

「这是我参与2022首次更文挑战的第33天,活动详情查看:2022首次更文挑战

658. 找到 K 个最接近的元素

题目

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

整数 aa 比整数 bb 更接近 xx 需要满足:

  • ax<bx|a - x| < |b - x| 或者
  • ax==bx|a - x| == |b - x|a<ba < 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]

提示

  • 1<=k<=arr.length1 <= k <= arr.length
  • 1<=arr.length <=1041 <= arr.length <= 10^4
  • arrarr 按 升序 排列
  • 104 <=arr[i],x<=10410^4 <= arr[i], x <= 10^4

题解

双指针

从数组左右两端向中间查找 kk 个目标元素;

  • leftleft表示数组左侧指针
  • rightright表示数组左侧指针
  • 如果 Math.abs(arr[left]x)<=Math.abs(arr[right]x)Math.abs(arr[left] - x) <= Math.abs(arr[right] - x) ;右指针向左移动一步
  • 否则左指针向右移动一步。

返回数组 [left,right][left,right] 区间数据即可

var findClosestElements = function (arr, k, x) {
  const len = arr.length
  if (len === 1) return arr
  if (k === len) return arr
  let left = 0
  let right = len - 1
  while (right - left >= k) {
    const l = arr[left]
    const r = arr[right]
    if (Math.abs(l - x) <= Math.abs(r - x)) {
      right--
    } else {
      left++
    }
  }

  return arr.slice(left, right + 1)
}

二分法

双指针有点慢,有更快的方法吗??二分

分析有序数组减去固定值x的绝对值,新数组排序可以归纳为下图中两种可能;

这就可以转换为找出数组中k个小元素,并且左侧元素优先

核心是xarr[mid]>arr[mid+k]xx - arr[mid] > arr[mid + k] - x

  • 如果 xarr[mid]>arr[mid+k]xx - arr[mid] > arr[mid + k] - x 最小的 k 个是落在[mid+1,right] [mid+1,right] 区间
  • 否则 最小的kk个是落在[left,mid] [left,mid] 区间
  • 最后返回的不是[left,right][left,right],而是 [left,left+k][left,left+k]

image.png

根据上述思路编辑代码如下

var findClosestElements = function (arr, k, x) {
  const len = arr.length
  let left = 0
  let right = len - k
  while (left < right) {
    const mid = left + ((right - left) >> 1)
    if (x - arr[mid] > arr[mid + k] - x) {
      left = mid + 1
    } else {
      right = mid
    }
  }

  return arr.slice(left, left + k)
}

结语

作者水平有限,如有不足欢迎指正;任何意见和建议欢迎评论区浏览讨论