《算法导论》第九章.中位数和顺序统计量

一.什么是中位数?

在一个n个元素顺序排列的集合中,一个中位数是它所属集合的中点元素

用公式表达中位数的位置就是:

1.当n为奇数时:
i=(n+1)/2;

2.当n为偶数时,有两个中位数:
i=n/2 ;
i=n/2+1;

因此,若不考虑n的奇偶性,中位数总是出现在 :i=(n+1)/2处和i=(n+2)/2处。

为了方便起见,以后博客的内容默认中位数在:(n+1)/2处

二.本章主要内容:

本章将讨论从一个有由n个互不相同的元素组成的集合中选择第i个顺序统计量(就是在顺序的排序的下第i个元素)的问题。
于是我们可以对输入和输出有如下的定义:

输入:一个包含n个数的集合A和一个整数i,1<=i<=n。
输出:元素x,且在A中恰好有i-1个小于它的元素。

我们很容易的知道可以通过排序算法将数组A排个序,然后找出第i个元素就可以解决这个问题。但是有没有更快的算法呢?

当然有,这个就是本章的内容。

三.具体策略:

1.特例:最小值和最大值
我们要知道:如果我们为了确定n个元素中的最小值或最大值,必须要做n-1次比较。

那么如果我们要同时确定最大值和最小值,就一定要进行2n-2次比较吗?

事实上,我们有只需要3n/2次比较就可以同时找到最大值和最小值的方法!!

做法如下:
(1)首先,我们将一对输入元素相互进行比较
(2)然后把较小的与当前最小值比较,把最大的与当前最大值进行比较。
这样,对每两个元素共需三次比较。

那么如何设定当前的最小值和最大值呢?
这个取决于n是奇数还是偶数:如果n是奇数,最大值最小值都是第一个元素。
如果n是偶数,就对前两个元素做一次比较来决定最大值最小值。

算法java代码实现:

public class MinAMax {
    int max;
    int min;
    public void MaxMin(int a[]){
        if(a.length%2==1){//如果数组a中元素有奇数个

            max=min=a[0];//将第一个元素默认为最大值和最小值

            for(int i=1;i<a.length;i+=2) {//从第二个元素开始成对向后循环,直到结束
                if(a[i]>a[i+1])//比较两个元素,如果前者大于后者,互换
                    Swap(a,i,i+1);
                if(a[i]<min)//将较小者与当前最小值比较
                    min=a[i];
                if(a[i+1]>max)//将较大者与当前最大值比较
                    max=a[i+1];
            }
        } else{//否则数组a中元素有偶数数个

            //将前两个元素的值赋给最大和最小值
            if(a[0]>a[1]) {
                max=a[0];
                min=a[1];
            } else{
                max=a[1];
                min=a[0];
            }
            for(int i=2;i<a.length;i+=2){//从第三个元素开始成对向后循环,直到结束
                if(a[i]>a[i+1])
                    Swap(a,i,i+1);
                if(a[i]<min)
                    min=a[i];
                if(a[i+1]>max)
                    max=a[i+1];
            }
        }
    }
    //交换函数,事项数组中两个i,j位置元素互换
    void Swap(int a[],int j,int i){
        int m;
        m=a[i];
        a[i]=a[j];
        a[j]=m;
    }

    public static void main(String[] args) {
        int a[]={3,2,65,6,8,6};
        MinAMax text=new MinAMax();
        text.MaxMin(a);
        System.out.println("最大值为:"+text.max);
        System.out.println("最小值为:"+text.min);
    }
}

结果:
在这里插入图片描述

2.一般情况:
主要是分治思想
用到了快排的随机key值的分区方法:
在这里插入图片描述
这个方法的时间复杂度仅为O(n);
方法代码:

 /*
     查找第i大元素方法
     参数:a:输入数组  p:开始位置  r:结束位置
     基本思想:
     用到分治思想
    */

    /**
     * 此方法返回数组中第i小的元素
     * @param a 输入数组
     * @param start 开始索引
     * @param end 结束索引
     * @param i 规定输出第i小元素
     * @return
     */
    public int RandonMized_select(int[] a, int start, int end, int i) {
        if (start == end)
            return a[start];
        int keyIndex=Randomized_partition(a, start, end);

//        for (int n:a){
//            System.out.print(n+" ");
//        }
        int k=keyIndex-start+1;
        //System.out.println("["+a[k]+"]");

        if(i==k){
            return a[keyIndex];
        }else if(i<k){
            return RandonMized_select(a, start,keyIndex-1,i);
        }else {
            return RandonMized_select(a, keyIndex + 1, end, i - k);
        }
    }
    //快排一次过程
    int partition(int[] a, int start, int end) {
        int key=a[end];
        int i=start-1;
        for(int j = start; j<end;j++) {
            if(a[j]<=key) {
                i=i+1;
                Swap(a,i,j);
            }
        }
        Swap(a,i+1, end);
        return i+1;
    }

    //随机产生key值进行一次快排分区
    int Randomized_partition(int[] a, int start, int end)
    {
        int i = (int)(Math.random() * (end));
        if(i<start){
            i=start;
        }
        Swap(a,end,i);
        return partition(a,start,end);
    }

测试代码:

public class MinAMax {
    int max;
    int min;
    public static void main(String[] args) {
        int a[]={3,2,65,6,8,9,7};
        MinAMax text=new MinAMax();
        int target=text.RandonMized_select(a,0,a.length-1,4);//在一个数组的0到a.length位置区域中找第4大的元素 
        System.out.println(target);

结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值