第K小的数值对(排序求解O(N * logN)、BFPRT求解O(N))

长度为N的数组arr,一定可以组成N^2个数值对
例如:arr = [3, 1, 2],
数值对有(3, 3)(3, 1)(3, 2)(1, 3)(1, 1)(1, 2)(2, 3)(2, 1)(2, 2),也就是任意两个数都有数值对,而且自己和自己也算数值对
数值对的排序规则,第一维数据从小到大,第一维数据一样的,第二维数据也从小到大
上面的数值对排序结果为:(1, 1)(1, 2)(1, 3)(2, 1)(2, 2)(2, 3)(3, 1)(3, 2)(3, 3)
给定一个数组arr,和整数k,返回第k小的数值对

复杂度为 O(N * logN) 的解法

/* 思路:对数组进行排序,
        计算第一维数据的位置,
        统计小于和等于第一维数据的值的数量
        计算第二维数据的位置
*/

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 时间复杂度为 O(N*logN)
vector<int> getPairKth(vector<int>& arr, int k) {
    
    
	if (arr.empty() || arr.size() * arr.size() <= k || k < 0) {
    
    
		return vector<int>();
	}
	// 排序时间复杂度为 O(N*logN)
	sort(arr.begin(), arr.end());
	// pair的第一维
	int firstDim = k / arr.size();

	// 统计小于第一维数据的数量和等于第一维数据的数量
	int lessFirst = 0;
	int equalFirst = 0;
	for (int i = 0; i < arr.size() && arr.at(i) <= arr.at(firstDim); i++) {
    
    
		arr.at(i) == arr.at(firstDim) ? equalFirst++ : lessFirst++;
	}

	int secondDim = (k - lessFirst * arr.size()) / equalFirst;
	vector<int> res{
    
     arr.at(firstDim), arr.at(secondDim) };
	return res;
}

int main() {
    
    
	vector<int> arr{
    
     3, 1, 2 };
	vector<int> pairKth = getPairKth(arr, 5);
	if (!pairKth.empty()) {
    
    
		cout << "(" << pairKth.at(0) << ", " << pairKth.at(1) << ")" << endl;
	}
	else {
    
    
		cout << "pairKth.empty() == true" << endl;
	}

	system("pause");
	return 0;
}

时间复杂度为 O(N) 的解法

/* 思路:不再对数组进行排序,
        计算第一维数据的位置(利用BFPRT在O(N)时间复杂度内找到该数据)
        统计小于和等于第一维数据的值的数量(时间复杂度:O(N))
        计算第二维数据的位置(利用BFPRT在O(N)时间复杂度内找到该数据) 
*/

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// BFPRT算法在无序数组中找到第K小(或大)的数时间复杂度为 O(N),前面的文章有详细的解释
class BFPRT {
    
    
public:
	int getMinKthByBFPRT(vector<int>& arr, int k, int left, int right) {
    
    
		if (arr.empty() || k >= arr.size() || left > right || k < left || k > right) {
    
    
			return -1;
		}
		if (left == right) {
    
    
			return arr.at(k);
		}
		int pivot = medianOfMedians(arr, left, right);
		vector<int> range = partition(arr, left, right, pivot);
		if (k > range.at(0) && k < range.at(1)) {
    
    
			return arr.at(k);
		}
		return k <= range.at(0) ? getMinKthByBFPRT(arr, k, left, range.at(0)) :
			getMinKthByBFPRT(arr, k, range.at(1), right);
	}

	int medianOfMedians(vector<int>& arr, int left, int right) {
    
    
		vector<int> medians;
		int offset = (right - left + 1) % 5 == 0 ? 0 : 1;
		int groupNums = (right - left + 1) / 5 + offset;
		for (int i = 0; i < groupNums; i++) {
    
    
			int tmpL = left + i * 5;
			int tmpR = tmpL + 5;
			sort(arr.begin() + tmpL, arr.begin() + min(tmpR, right));
			medians.push_back(arr.at(tmpL + ((min(tmpR, right) - tmpL) >> 1)));
		}
		return getMinKthByBFPRT(medians, medians.size() / 2, 0, medians.size() - 1);
	}

	vector<int> partition(vector<int>& arr, int left, int right, int pivot) {
    
    
		int tmpL = left - 1;
		int tmpR = right + 1;
		int index = left;
		while (index < tmpR) {
    
    
			if (arr.at(index) < pivot) {
    
    
				swap(arr, index++, ++tmpL);
			}
			else if (arr.at(index) > pivot) {
    
    
				swap(arr, index, --tmpR);
			}
			else {
    
    
				index++;
			}
		}
		vector<int> res{
    
     tmpL, tmpR };
		return res;
	}

	void swap(vector<int>& arr, int n1, int n2) {
    
    
		int tmp = arr.at(n1);
		arr.at(n1) = arr.at(n2);
		arr.at(n2) = tmp;
	}
};

// 时间复杂度为 O(N)
vector<int> getPairKth(vector<int>& arr, int k) {
    
    
	if (arr.empty() || k < 0 || k >= arr.size() * arr.size()) {
    
    
		return vector<int>();
	}
	int firstDim = k / arr.size();
	// BFPRT获取第K小的数时间复杂度为 O(N)
	int firstNum = BFPRT().getMinKthByBFPRT(arr, firstDim, 0, arr.size() - 1);

	// 获取小于和等于第一维数据数量 时间复杂度为 O(N)
	int lessFirstNums = 0;
	int equalFirstNums = 0;
	for (int i = 0; i < arr.size() && arr.at(i) <= firstNum; i++) {
    
    
		arr.at(i) == firstNum ? equalFirstNums++ : lessFirstNums++;
	}

	int secondDim = (k - lessFirstNums * arr.size()) / equalFirstNums;
	// BFPRT获取第K小的数时间复杂度为 O(N)
	int secondNum = BFPRT().getMinKthByBFPRT(arr, secondDim, 0, arr.size() - 1);
	vector<int> pairKth{
    
     firstNum, secondNum };
	return pairKth;
}

int main() {
    
    
	vector<int> arr{
    
     3, 1, 2 };
	vector<int> pairKth = getPairKth(arr, 5);
	if (!pairKth.empty()) {
    
    
		cout << "(" << pairKth.at(0) << ", " << pairKth.at(1) << ")" << endl;
	}
	else {
    
    
		cout << "pairKth.empty() == true" << endl;
	}

	system("pause");
	return 0;
}

如有侵权,请联系删除,如有错误,欢迎大家指正,谢谢

猜你喜欢

转载自blog.csdn.net/xiao_ma_nong_last/article/details/105756842