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

  • 中位数

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

  • 中位数寻找的快速算法

一般寻找中位数可以先将数组排序,按照次序将中间的数据作为中位数即可,其时间复杂度主要取决于排序算法的时间复杂度,利用快速排序可以将其控制为线性对数量级。
但是能否打破线性对数的限制呢?其中最关键的问题是,寻找中位数并不需要整个数组完全有序,如果把多余的元素排序省略掉,那么就可以超越线性对数的限制实现最快的算法。
启发来源于快速排序算法中的切分法,比如我们需要找到数组中第 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));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值