1. 程式人生 > >BFPRT尋找一群數中第k大的數

BFPRT尋找一群數中第k大的數

思路:用快排中partition函式的思想,一直partition到等於軸心數的區域包含arr[arrSize - k],軸心數的選取為比較特殊。由於保證了軸心數的品質,所以BFPRT演算法的時間複雜度是嚴格的O(n)。

軸心數的選取

  1. 分組:把partition的區域,每5個分成一組,最後不滿5個的單獨成一組
  2. 組內排序:每組數進行排序。
  3. 組成中位數陣列:取出每組數的中位數,最後一組如果為偶數,取前面的那一個。
  4. 取出中位數陣列的中位數:中位數資料進行排序,取其中位數

程式碼如下:

//groupNums:滿5個數的組的數量  lastGroupNum:最後一組數的個數
//medians: 中位數陣列
int getMedianOfmedians(int arr[], const int leftIndex, const int rightIndex) {
	int groupNums = (rightIndex - leftIndex + 1) / 5;
	int lastGroupNum = (rightIndex - leftIndex + 1) % 5;
	vector<int> medians;
	for (int i = 0; i < groupNums; i++) {
		sort(arr + leftIndex + i * 5, arr + leftIndex + i * 5 + 4);
		medians.push_back(arr[leftIndex + i * 5 + 2]);
	}
	if (lastGroupNum) {
		sort(arr + rightIndex - lastGroupNum + 1, arr + rightIndex);
		medians.push_back(arr[rightIndex - lastGroupNum / 2]);
	}
	sort(medians.begin(), medians.end());
	return medians[(medians.size() - 1) / 2];
}

 partition部分

假設求第100大的數,那個排序之後該數就該在陣列中的下標就該為1000 - 100 = 900 

//partition過程,返回新子區間的上下界
vector<int> partition(int arr[], int leftIndex, int rightIndex) {
	int lessBoundary = leftIndex - 1;
	int biggerBoundary = rightIndex + 1;
	int curIndex = leftIndex;
	int pivotNum = getMedianOfmedians(arr, leftIndex, rightIndex);

	while (curIndex < biggerBoundary) {
		if (arr[curIndex] < pivotNum) {
			swap(arr[curIndex++], arr[++lessBoundary]);
		}
		else if (arr[curIndex] > pivotNum) {
			swap(arr[curIndex], arr[--biggerBoundary]);
		}
		else {
			curIndex++;
		}
	}
	vector<int> leftRightIndex(2);
	leftRightIndex[0] = lessBoundary;
	leftRightIndex[1] = biggerBoundary;
	return leftRightIndex;
}


//kthIndex:第k大的數在陣列中應該在的位置
//equalRange為本次partition得到的等於x的區間
//kthIndex在equalRange中停止,否則去子區間尋找
int process(int arr[], int kthIndex, int leftIndex, int rightIndex) {
	vector<int> equalRange = partition(arr, leftIndex, rightIndex);
	if (kthIndex < equalRange[1] && kthIndex > equalRange[0]) {
		return arr[kthIndex];
	}
	else if (kthIndex <= equalRange[0]) {
		return process(arr, kthIndex, leftIndex, equalRange[0]);
	}
	else{
		return process(arr, kthIndex, equalRange[1], rightIndex);
	}
}

int calKthMaxByPartition(int arr[], int k, int arrSize) {
	return process(arr, arrSize - k, 0, arrSize - 1);
}