【Lintcode】460. Find K Closest Elements

题目地址:

https://www.lintcode.com/problem/find-k-closest-elements/description

给定一个升序数组 A A A,再给定一个目标数 t t t,和一个非负整数 k k k,找离 t t t最近的 k k k个整数。需要按照与 t t t的接近程度从小到大排序。如果一样接近,则小的排在前面。

首先,离 t t t最近的 k k k个数一定在 A A A中位置连续(反证法可证),我们可以对这 k k k个数的左端点(下称合法区间左端点)位置进行二分。设左端点一开始的范围是 [ l , r ] [l,r] [l,r],显然 l = 0 , r = l A − k l=0, r=l_A-k l=0,r=lAk。取范围中点 m m m,考虑 A [ m ] A[m] A[m] A [ m + k ] A[m+k] A[m+k]这两个数字(当然如果 A [ m + k ] A[m+k] A[m+k]不存在,那直接向左找,即令 r = m r=m r=m,然而在代码里却不用考虑这个问题,因为我们取的是靠左的中点,也就是说 m m m不会取到 l A − k l_A-k lAk),如果 A [ m ] A[m] A[m] A [ m + k ] A[m+k] A[m+k] t t t更近或者一样近,那么合法区间左端点的位置应该在 [ l , m ] [l,m] [l,m];否则合法区间左端点应该在 [ m + 1 , r ] [m+1,r] [m+1,r]。找到合法区间左端点后,哪 k k k个数就确定了,可以用双指针算法将它们按照谁离的远(一样远就比谁更大)加入最后答案,最后再逆序一下即可。代码如下:

public class Solution {
    /**
     * @param A: an integer array
     * @param target: An integer
     * @param k: An integer
     * @return: an integer array
     */
    public int[] kClosestNumbers(int[] A, int target, int k) {
        // write your code here
        int[] res = new int[k];
        if (k == 0) {
            return res;
        }
        
        int l = 0, r = A.length - k;
        while (l < r) {
            int m = l + (r - l >> 1);
            int left = A[m], right = A[m + k];
            if (Math.abs(left - target) <= Math.abs(right - target)) {
                r = m;
            } else {
                l = m + 1;
            }
        }
    
    	// 最后用双指针算法将这k个数加入res
        r = l + k - 1;
        int idx = 0;
        while (l <= r) {
            if (Math.abs(A[l] - target) <= Math.abs(A[r] - target)) {
                res[idx++] = A[r--];
            } else {
                res[idx++] = A[l++];
            }
        }
        
        // 最后反个序
        reverse(res);
        return res;
    }
    
    private void reverse(int[] A) {
        for (int i = 0, j = A.length - 1; i < j; i++, j--) {
            swap(A, i, j);
        }
    }
    
    private void swap(int[] A, int i, int j) {
        int tmp = A[i];
        A[i] = A[j];
        A[j] = tmp;
    }
}

时间复杂度 O ( log ⁡ ( l A − k ) + k ) O(\log (l_A-k)+k) O(log(lAk)+k),空间 O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值