Find K Closest Elements 发现K最近的元素

给定一个有序数组,两个整数k和x,找到数组中最接近x的元素。 结果也应按升序排序。 如果有平局,小元素总是首选。

例1:
输入:[1,2,3,4,5],k = 4,x = 3
输出:[1,2,3,4]
例2:
输入:[1,2,3,4,5],k = 4,x = -1
输出:[1,2,3,4]
注意:
值k是正数,并且总是小于排序数组的长度。
给定数组的长度是正数,不会超过104
数组和x中元素的绝对值不会超过104

思路1:通过二分查找的变种解法,先找到第一个大于等于x的数字,然后左右比较谁离x近,就依次返回对应的下标,一直左右延伸到res中有k个数字。

参考代码:

    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
	vector<int> res;
	if (arr[0] >=x ) {
		for (int i = 0; i < k; i++) {
			res.push_back(arr[i]);
		}
		return res;
	}
	if (arr[arr.size()-1] <= x) {
		for (int i = (arr.size() - k); i < arr.size(); i++) {
			res.push_back(arr[i]);
		}
		return res;
	}
	int left = 0;
	int right = arr.size() - 1;
	while (left < right) {
		int mid = left + (right - left) / 2;
		if (arr[mid] < x) {
			left = mid + 1;
		}
		else {
			right = mid;
		}
	}
	int slow = left-1;
	int fast = left;
	while (res.size() != k && slow>=0 && fast<arr.size()) {
		if (abs(arr[slow] - x) < abs(arr[fast] - x)) {
			res.push_back(arr[slow]);
			slow--;
		}
		else if (abs(arr[slow] - x) > abs(arr[fast] - x)) {
			res.push_back(arr[fast]);
			fast++;
		}
		else {
            if(slow==fast){
                			res.push_back(arr[slow]);
			slow--;
            fast++;
            }
            else{
                			res.push_back(arr[slow]);
			slow--;                
            }

		}
	}
	if (res.size() == k) {
		sort(res.begin(), res.end());
		return res;
	}
	while (res.size() != k) {
		if (slow >= 0) {
			res.push_back(arr[slow]);
			slow--;
		}
		else {
			res.push_back(arr[fast]);
			fast++;
		}
	}
	sort(res.begin(), res.end());
	return res;        
    }

思路二:也是二分法,可以直接返回需要的k个数字的最左的下标,首先初始化left=0,right=arr.size()-k,保证在二分过程中不会越界,每次比较arr[mid]和arr[mid+k]哪个离x近,如果arr[mid+k]近,那么意味着如下不变约束成立:

abs(arr[mid+k+d]-target)<abs(arr[mid+d]-target)  0<=d<k

即对以arr[mid+k]为起点向右数k个数肯定比以arr[mid]为起点向右数k个数更靠近target。所以以arr[mid]为起点及小于mid的部分肯定不是最优解,所以抛弃mid的左半部分(包含mid),即left=mid+1。同理对于另外一边也是相同的思想,最后的结论是right=mid。这里要注意死循环处理。即:

abs(arr[mid+k+d]-target)>=abs(arr[mid+d]-target)

这里一定要包含等于符号,否则对于如下的输入[1,2,3,4,5] k=4,target=3。最后会出现两个满足条件的结果[1,2,3,4]和[2,3,4,5]由于题目要求当距离相等时,优先考虑左边,所以等于符号的左右便是当出现距离的情况是,让right=mid,也罢右边界收缩到mid,从而保留左半部分。

参考代码如下:

    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
	int left = 0;
	int right = arr.size() - k;
	while (left < right) {
		int mid = left + (right - left) / 2;
		if (abs(arr[mid] - x) <= abs(arr[mid + k] - x)) {
			right = mid;
		}
		else {
			left = mid+1;
		}
	}
	return vector<int>(arr.begin()+left, arr.begin() + left+k);
    }










猜你喜欢

转载自blog.csdn.net/qq_26410101/article/details/80722945