您的位置:首页 > 其它

快速切分法寻找中位数的递归与非递归实现

2015-11-01 21:31 344 查看
中位数

对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果观察值有偶数个,则中位数不唯一,通常取最中间的两个数值的平均数作为中位数。

中位数寻找的快速算法

一般寻找中位数可以先将数组排序,按照次序将中间的数据作为中位数即可,其时间复杂度主要取决于排序算法的时间复杂度,利用快速排序可以将其控制为线性对数量级。

但是能否打破线性对数的限制呢?其中最关键的问题是,寻找中位数并不需要整个数组完全有序,如果把多余的元素排序省略掉,那么就可以超越线性对数的限制实现最快的算法。

启发来源于快速排序算法中的切分法,比如我们需要找到数组中第
k
小的元素,首先将数组
a[lo,hi]
切分返回整数
j
,使得
a[lo,j-1]
都小于等于
a[j]
,而
a[j+1,hi]
都大于等于
a[j]
,如果
j==k
,那么
j
位置的元素就是我们要找的第
k
小的元素,而如果
j>k
,就要切分左子数组,如果
j<k
,就要切分右子数组,不断缩小选定的子数组的规模直到只剩下一个元素,则它就是最终我们要找的第
k
小的元素。

经过数学推导,这种快速切分法寻找中位数仅仅为线性量级,是寻找中位数最为快速的算法。

算法实现

public class Select {
// 寻找中位数
public static <T> Comparable<T> findMedium(Comparable<T>[] a) {
return select1(a, a.length / 2);
}

// 找出数组中第k小的元素,非递归实现
public static <T> Comparable<T> select1(Comparable<T>[] a, int k) {
int lo = 0, hi = a.length - 1;
while (hi > lo) {
int j = partition(a, lo, hi);
if (j == k) {
return a[k];
} else if (j > k) {
hi = j - 1;
} else if (j < k) {
lo = j + 1;
}
}
return a[k];
}

// 找出数组中第k小的元素,递归实现
public static <T> Comparable<T> select2(Comparable<T>[] a, int k, int lo, int hi) {
int j = partition(a, lo, hi);
if (j == k) {
return a[k];
} else if (j > k) {
return select2(a, k, lo, j - 1);
} else {
return select2(a, k, j + 1, hi);
}
}

public static <T> int partition(Comparable<T>[] a, int lo, int hi) {
int i = lo, j = hi + 1;
Comparable<T> 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);// 将切分元素放在切分位置
return j;
}

@SuppressWarnings("unchecked")
public static <T> boolean less(Comparable<T> v, Comparable<T> w) {
return v.compareTo((T) w) < 0;
}

private static <T> void exch(Comparable<T>[] a, int i, int j) {
Comparable<T> t = a[i];
a[i] = a[j];
a[j] = t;
}

public static void main(String[] args) {
String[] a = "qwertyuiopasdfghjklzxcvbnm".split("");
System.out.println("Max: "+select1(a, 0));
System.out.println("Min: "+select2(a, 25, 0, a.length - 1));
System.out.println("Medium: "+findMedium(a));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: