找出一个组元素中的中位数(中间值,它不大于一半的元素也不小于另一半的元素),是一个和排序有关但又不需要完全排序的重要应用。查找中位数在统计和许多数据处理中很常见。
可以把查找中位数看成是一种特殊的选择:在数组中查找第k个小或大的元素。我们可以回想前面《快速排序及改进》中的partition(Comparable[] a,int lo,int hi)方法,它会将数组的a[lo]到a[hi]重新排列(局部排序)并返回一个整数j,使得:
那么,要获取第K小的值,当k = j时,a[j]就是要求解的数值了。
对于查找中位数,就是k = N/2时的a[j],a[j]就是要求的中位数。整个代码如下:
可以把查找中位数看成是一种特殊的选择:在数组中查找第k个小或大的元素。我们可以回想前面《快速排序及改进》中的partition(Comparable[] a,int lo,int hi)方法,它会将数组的a[lo]到a[hi]重新排列(局部排序)并返回一个整数j,使得:
a[lo ... j - 1] <= a[j] <= a[j + 1 ... hi]
那么,要获取第K小的值,当k = j时,a[j]就是要求解的数值了。
/**
*查找一个数组中的第k小的元素
*/
public static Comparable select(Comparable[] a,int k){
//对数组a洗牌
shuffle(a);
int lo = 0;
int hi = a.length - 1;
int j = 0;
while(hi > lo){
j = partition(a,lo,hi - 1);
if(j == k){
return a[k];
}else if(j > k){
hi = j - 1;
}else{
lo = j + 1;
}
}
return a[k];
}
对于查找中位数,就是k = N/2时的a[j],a[j]就是要求的中位数。整个代码如下:
/**
* 取中位数
*/
public class MedianFinder extends SortBase{
/**
* 局部排序,查找数组a的中位数
* @param a
* @return
*/
public static Comparable find(Comparable[] a){
int len = a.length;
int mid = 0;
if((isOdd(len)){//奇数
mid = (len + 1)/2;
}else{
mid = len/2;
}
return select(a,mid - 1);
}
/**
* 判断整数i是否是奇数
* @param i
* @return true:奇数,false:偶数
*/
private static isOdd(int i){
return (k & 1) != 0;
}
/**
* 查找数组a中第k小的元素
* @param a
* @param k
* @return
*/
public static Comparable select(Comparable[] a,int k){
shuffle(a);
Comparable val = null;
int lo = 0;
int hi = a.length - 1;
int j = 0;
while(hi > lo){
j = partition(a,lo,hi);
if(j == k){
val = a[k];
break;
}else if(j > k){
hi = j - 1;
}else{
lo = j + 1;
}
}
return val;
}
/**
* 在数组a的下标lo和hi范围内获取快速切分的基数的小标,使该基数的左边的
* 元素都不大于该数,而右边的元素都不小于该数
* @param a
* @param lo
* @param hi
* @return 快速切分基数的下标
*/
public static int partition(Comparable[] a,int lo,int hi){
//将数组切分为a[lo...i - 1],a[i],a[j + 1,hi]
int i = lo,j = hi + 1; //设置左右扫描指针
Comparable v = a[lo]; //切分元素
while(true){
while(less(a[++i],v)){
if(i == hi) break;
}
while(less(v,a[--j])){
if(j == lo) break;
}
if(i >= j){
break;
}
exch(a,i,j);
}
exch(a,lo,j); //将v = a[i]放入正确的位置
//a[lo...j - 1] <= a[j] <= a[j + 1 ... hi]
return j;
}
}