快排衍生之「利用快排思想求无序数组中位数」

题目:利用快排思想求一个无序数组的中位数

对于一个有序数组arr来说:

  • 如果数组长度为奇数,中位数为中间的那个数,即arr[arr.length/2]
  • 如果数组长度为偶数,中位数为中间的两个数的平均值,即(arr[length/2] + arr[length/2-1]) / 2

那么对于一个无序数组来说,应该如何求它的中位数 ?

这个问题可以抽象化为寻找第k大的数,具体到寻找中位数,可分为两种情况:

  • 如果数组长度为奇数,即为寻找第arr.length/2大的数;
  • 如果为偶数,则为分别寻找第arr.length/2+1和第arr.length/2大的数,然后求其平均值。

我们可以使用快排思想快速找中位数,即先挑选一个数作为标准(pivot),以该元素为支点,将数组划分为两部分。快排每排完一轮之后左侧都是比它小的元素,右侧都是比它大的元素,那么支点的index(假设为N-1)即为第N大的数。当N== K,我们就找到了第K大的数;当N > K时, 第K大的数在[0,N-1]范围内;当N < K时,第K大的数在[N+1,n-1](n为数组长度)范围内,利用递归即可找到第K大的数。

注意:这里第k大的数,是下标为k-1的值,即arr[k-1]

Java 实现如下:

/**
 * 利用快排思想求无序数组中位数
 * @Author Hory
 * @Date 2020/9/8
 */
public class Solution {
    /**
     * 寻找无序数组第k大的数,其实就是找排序后的数组的 arr[k-1]
     * 这里用到了递归
     * 明确函数意义:该方法实现了找到第k大的数,该方法没有返回值,通过不断调整数组,最终使得第k大的值位于索引k-1
     * @param arr
     * @param k
     * @param start
     * @param end
     * @return
     */
    private static void selectKthNum(int[] arr, int k, int start, int end){
        if(arr == null || k < 0 || start >= end) return;
        int left  = start;
        int right = end;
        int pivot = arr[start];
        while(left < right){
            while(left < right && arr[right] >= pivot) right--;
            arr[left] = arr[right];
            while(left < right && arr[left] <= pivot) left++;
            arr[right] = arr[left];
        }
        arr[left] = pivot;
        if(left == k) return;
        else if(left < k) selectKthNum(arr,k,left+1,end);
        else selectKthNum(arr,k,start,left-1);
    }

    /**
     * @param arr
     * @return
     */
    public static double findMedian(int[] arr){
        if(arr.length <= 0) return -1;
        int length = arr.length;
        if(length % 2 == 1) {  // 如果数组长度为奇数,只需返回中间的数字即可
            selectKthNum(arr,length/2,0,length-1);
            return arr[(length)/2];
        }else {   //如果数组长度为偶数,返回中间两个数字的平均值
            selectKthNum(arr,length/2-1,0,length-1);
            selectKthNum(arr,length/2,0,length-1);
            return (arr[length/2] + arr[length/2-1])/2.0;
        }
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值