携手创作,共同成长!这是我参与「掘金日新计划 · 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
}