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

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

题目描述:

给定一个排序好的数组 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]

解题分析:

方法一:双指针

以 arr = [1, 2, 3, 4, 5] , x = 3, k = 4 为例。
删除法。由题意可得,最终需要最接近 3 的 4 个数,而因为是有序数组,且返回的是连续升序子数组,所以删除掉边界的那一个数即可;那么就可以使用双指针的方式来保留区间。
只需要比较 arr[0] 和 arr[4] 与 x 的差值大小即可,删除差值大的那一个,差值相同时保留数组中下标小的那一个,所以例题答案为 [1,2,3,4] 。

时间复杂度:O(n)

class Solution {
public:
    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
        int n = arr.size() - 1;
        int l = 0, r = n;
        k --;
        while(r - l > k) {
            if(abs(arr[l] - x) > abs(arr[r] - x)) 
                //缩小区间,前指针右移,删除前边界的数
                l ++;
            else 
                //缩小区间,后指针左移,删除后边界的数
                r --;    
        }

        vector<int> res;
        while(l <= r)  res.push_back(arr[l ++]);
        return res;
    }
};

方法二:二分法

题目要求接近 x 的 k 个数,要求返回的是区间,并且是连续区间;
也就是说,只要我们找到了左边界的下标,然后从左边界开始数 k 个数,返回就好了。
因此,我们可以先给出左边界所在区间 [0, size - k],保证向后取 k 个数,然后不断缩小区间范围,直至得到左边界的下标,就可以满足题意。
我们可以从 [0, size - k] 这个区间的**中间位置(arr[mid])**开始,定位一个长度为 (k + 1) 的区间,根据他们与 x 的差值大小,来判断区间的合理性,从而不断缩小区间。

时间复杂度: O(logn+k)

class Solution {
public:
    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
        //左边界区间取值为[0, size - k]
        int l = 0, r = arr.size() - k;
        while (l < r) {
            //从左边界向后去取k个数
            int mid = l + (r - l) / 2;
            //使用二分法确定左边界的值
            if (x - arr[mid] > arr[mid + k] - x) {
                //区间左边过大,右移缩小
                l = mid + 1;
            }
            //左移缩小
            else {
                r = mid;
            }
        }
        vector<int> res;
        for (int i = l; i < l + k; i++){
            res.push_back(arr[i]);
        }   
        return res;  
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值