一、题目
给定一个 排序好 的数组 arr ,两个整数 k 和 x ,从数组中找到最靠近 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]
作者:力扣 (LeetCode) 链接:leetcode.cn/leetbook/re… 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二、思路
双指针解法
- 根据题意,算数组中与x值绝对距离最近的k个值
- 使用双指针,首先定义长度len,右指针left,左指针right
- while的判断条件是k < right - left
- 左右指针通过距离的判断向中间移动,直到条件不成立
- 由于是有序数组,
- 所以左边值得距离为x - arr[left],右边值得距离为arr[right-1] - x
- 因此当左指针的距离小于右指针的距离是 右指针--
- 否则左指针++
- 最后通过slice(left, right)返回k个数
双指针+二分查找
- 双指针思路还是定义左右两个指针,但是这循环不再是循环整个数据
- 循环的方式使用二分查找
- 首先先定义len left right 定义中间值mid
- 由于要找到k个数,因此这里的对比是x-arr[mid] 与arr[mid+k]-x
- 对比双方,如果x-arr[mid] 大于arr[mid+k]-x时,说明右区间小,左指针等于mid+1
- 否则就定义了右指针的区间
- 这里有个问题,是这里的if判断为啥必须用x - arr[mid] > (arr[mid + k] - x)
- 如果有兴趣可以输出下这个值,这里有个问题是mid + k的是有时候会大于len,因此arr[mid + k]的值是undefined,减x后为NaN
- NaN与任意值对比都为false,这是如果使用x - arr[mid] < (arr[mid + k] - x)判断,那么就直接走入left = mid + 1,
- 在len不足的情况下,循环就直接结束了, 因此这里只能先判断大于,不能判断小于
- 之后循环在left < right条件不满足时结束,这里left与left+k这个区间就是我们想要的值
- 返回arr.slice(left, left+k)
三、代码
双指针解法
let findClosestElements = function(arr, k, x) {
/**
* 根据题意,算数组中与x值绝对距离最近的k个值
* 使用双指针,首先定义长度len,右指针left,左指针right
* while的判断条件是k < right - left
* 左右指针通过距离的判断向中间移动,直到条件不成立
* 由于是有序数组,
* 所以左边值得距离为x - arr[left],右边值得距离为arr[right-1] - x
* 因此当左指针的距离小于右指针的距离是 右指针--
* 否则左指针++
* 最后通过slice(left, right)返回k个数
* */
let len = arr.length, left = 0, right = len
while (k < right - left) {
const rightNum = arr[right-1] - x, leftNum = x - arr[left]
if (rightNum < leftNum) {
left++
} else {
right--
}
}
return arr.slice(left, right)
}
findClosestElements(arr, k, x)
双指针+二分查找
let findClosestElements = function(arr, k, x) {
/**
* 双指针+二分查找
* 双指针思路还是定义左右两个指针,但是这循环不再是循环整个数据
* 循环的方式使用二分查找
* 首先先定义len left right 定义中间值mid
* 由于要找到k个数,因此这里的对比是x-arr[mid] 与arr[mid+k]-x
* 对比双方,如果x-arr[mid] 大于arr[mid+k]-x时,说明右区间小,左指针等于mid+1
* 否则就定义了右指针的区间
*
* 这里有个问题,是这里的if判断为啥必须用x - arr[mid] > (arr[mid + k] - x)
* 如果有兴趣可以输出下这个值,这里有个问题是mid + k的是有时候会大于len,因此arr[mid + k]的值是undefined,减x后为NaN
* NaN与任意值对比都为false,这是如果使用x - arr[mid] < (arr[mid + k] - x)判断,那么就直接走入left = mid + 1,
* 在len不足的情况下,循环就直接结束了, 因此这里只能先判断大于,不能判断小于
*
* 之后循环在left < right条件不满足时结束,这里left与left+k这个区间就是我们想要的值
* 返回arr.slice(left, left+k)
* */
let len = arr.length, left = 0, right = len
while (left < right) {
const mid = left + Math.floor((right - left -1)>>1)
if(x - arr[mid] > (arr[mid + k] - x)) {
left = mid + 1
} else {
right = mid
}
}
return arr.slice(left, left+k)
}
findClosestElements(arr, k, x)