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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值