找到K个最接近的元素

108 阅读2分钟

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

描述

给你一个已经排序好的数组arr,两个整数k和x,从给定的数组arr中找出最靠近x的k个数,最靠近的意思可以理解为两者减操作绝对值最小则最靠近。需要返回包含这k个元素的数组,并且是升序排列。

分析

对数组进行遍历是最容易想到的思路,找出k个最靠近的元素,那我们可以想象出一个窗口容器,仅容器的长度就是k,我们可以从头开始,不断往后找k个元素,并计算出这个窗口里面的值与x的距离总和,如果距离总和小于前一个窗口,则重新赋值,否则不用处理。

移动窗口这种方法时间复杂度是O(N²),运行效率不是很高。能否仅用O(N)的时间复杂度来解决呢?

那我会想到双指针。

大体的思路是双指针初始一个指向数组第一个元素,一个指向最后一个元素,不断往里压缩,直至仅存在k个元素为止。移动的规则是左指针如果大于右指针距离x值的话,则左指针小标加一;相反,如果左指针小于等于有指针距离x值,则右指针小标减一。最后剩下的k个元素就是最后需要返回的结果。

用双指针这种思路运行效率较高,最多仅需要一次遍历即可,其实它就相当于在排序好数组中进行了过滤操作,最后剩下的就是要找的k个元素。

程序实现

根据以上分析,我们展示下移动窗口具体代码实现。如下:

var findClosestElements = function(arr, k, x) {
  // 默认无穷大
  let sum = Infinity
  let ret = []
  for (let i = 0; i < arr.length - k + 1; i++) {
    // 求k个数的差值和
    let cal = 0
    for (let j = i; j < k + i; j++) {
      cal += Math.abs(arr[j] - x)
    }
    // 只有当下一个窗口距离总和大于前一个窗口才重新赋值,因为需要优先返回小标较小数组
    if (sum > cal) {
      sum = cal
      ret = arr.slice(i, k + i)
    }
  }
  return ret
}