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

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

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

题目

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

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

  • a x < b x |a - x| < |b - x| 或者
  • a x = = b x |a - x| == |b - x| a < b 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]
复制代码

提示

  • 1 < = k < = a r r . l e n g t h 1 <= k <= arr.length
  • 1 < = a r r . l e n g t h   < = 1 0 4 1 <= arr.length <= 10^4
  • a r r arr  按 升序 排列
  • 1 0 4   < = a r r [ i ] , x < = 1 0 4 10^4 <= arr[i], x <= 10^4

题解

双指针

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

  • l e f t left 表示数组左侧指针
  • r i g h t right 表示数组左侧指针
  • 如果 M a t h . a b s ( a r r [ l e f t ] x ) < = M a t h . a b s ( a r r [ r i g h t ] x ) Math.abs(arr[left] - x) <= Math.abs(arr[right] - x) ;右指针向左移动一步
  • 否则左指针向右移动一步。

返回数组 [ l e f t , r i g h t ] [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个小元素,并且左侧元素优先

核心是 x a r r [ m i d ] > a r r [ m i d + k ] x x - arr[mid] > arr[mid + k] - x

  • 如果 x a r r [ m i d ] > a r r [ m i d + k ] x x - arr[mid] > arr[mid + k] - x 最小的 k 个是落在 [ m i d + 1 , r i g h t ] [mid+1,right] 区间
  • 否则 最小的 k k 个是落在 [ l e f t , m i d ] [left,mid] 区间
  • 最后返回的不是 [ l e f t , r i g h t ] [left,right] ,而是 [ l e f t , l e f t + 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)
}
复制代码

结语

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

Guess you like

Origin juejin.im/post/7066359416852840479