左神算法笔记(十四)——BFPRT算法

解决的问题:在一个无序数组中找到第k小的数

快排解法:

  1. 随机选择一个数,利用荷兰国旗问题,将数据分为小于等于大于三块
  2. 利用区分好的数组计算长度,看是否命中,否则看是在哪个块中,在确定块中继续划分,重新回到1。
    对于这种解法而言,如果最差情况是O(N^2),一般情况下,用master公式计算出,最好是O(N)。最后的数学期望是O(N)。

暴力解法:

将整个数组全排列,形成一个有序数组,然后再找到第k个元素。

BFPRT算法:

BFPRT算法跟快排的算法只有在选取划分值的情况上不同,其他全部一样。
BFPRT算法:

  1. 分组(假设每五个一组,最后剩余的不到五个的一组)
  2. 分组之后每个小组之内排序,跨组不排序,五个数排序,总共需要划分的时间复杂度为O(N)。
  3. 将每个组的中位数拿出,构成新的数组,此时新数组长度为N/5(最后不到五个的可以拿上中位数,也可以拿下中位数)
  4. 调用BFPRT算法,此时递归过程中不再寻找k项,而是选择中间的中位数
  5. 下面就是利用上述num,进行荷兰国旗问题排序
每一步的时间复杂度:

第一步复杂度O(1),
第二步复杂度O(n),
第三部复杂度O(n),
第四步T(N/5),求出p
第五步O(N),
第六步可以确定好是正好还是前往左/右,
此时左边和右边的规模可以估计出来,而不再是未知的。

此时可以估计出至少有多少个数比p要大,或者比p小。
通过计算可以得知,按照三次排序,第一次排序N/5个人被挑选出来,再进行排序,此时N/10比数值大,比中位数大的数值此时加入到原始数组中,所以此时N/10+2N/10=3N/10,所以此时最多有7N/10比p大或者小。

代码实现

//用BFPRT方法得到第k个最小的值
public static int getMinKthByBFPRT(int[] arr,int k ){
	int[] copyArr = copyArray(arr);
	//得到数组中第k-1位置上的值就是第k小的值
	return bfprt(copyArr,0,copyArr.length-1,K-1);
}
//bfprt方法主体部分
public static int bfprt(int[] arr,int begin,int end,int i){
	if(begin == end){
		return arr[begin];
	}
	//求中位数的中位数
	int pivot = medianOfMedians(arr,begin,end);
	//求完第二轮的中位数之后就开始进行划分
	int[] privotRange = partition(arr,begin,end,pivot);
	//正好i位置等于相等部分则返回
	if(i>= pivotRange[0] && i <= pivotRange[1]){
		return arr[i];
		//i小于排序起始位置的情况
	}else if(i<pivotRange[0]){
		return bgprt(arr,begin,pivotRange[0]-1,i);
	}else{
		//i大于终止位置的情况
		return bfprt(arr,pivotRange[1]+1,end,i);
	}
}
	
public static int medianOfMedians(int[] arr,int begin,int end){
	int num = end - begin +1;
	int offset = num % 5 == 0 ? 0:1;
	int[] marr = new int[num/5+offest];
	for(int i = 0;i<mArr.length;i++){
		int beginI = begin + i*5;
		int endI = beginI +4;
		mArr[i] = getMedian(arr,beginI,Math.min(end,endI));
	}
	return bfprt(mArr,0,mArr.length-1,mArr.length/2);
}

//partition是实现荷兰国旗问题,将大于小于等于三类划分开
public static int[] partition(int[] arr,int begin,int end,int privotValue){
	int small = begin -1;
	int cur = begin;
	int big = end+1;
	while(cur != big){
		if(arr[cur]< pivotValue){
			swap(arr,++small,cur++);
		}else if(arr[cur]>pivotValue){
			swap(arr,cur,--big);
		}else{
			cur++;
		}
	}
	//range返回两个值,第一个是起始位置,第二个是排序的终止位置
	int[] range = new int[2];
	range[0] = samll+1;
	range[1] = big-1;
	return range;
}
		
		
发布了24 篇原创文章 · 获赞 6 · 访问量 490

猜你喜欢

转载自blog.csdn.net/qq_35065720/article/details/104206700