zoukankan      html  css  js  c++  java
  • 求中位数,O(n)的java实现【利用快速排序折半查找中位数】

    查找无序数组的中位数,要想时间复杂度为O(n)其实用计数排序就能很方便地实现,在此讨论使用快速排序进行定位的方法。

    1、中位数定义

    2、算法思想

    3、Java代码实现

    4、时间复杂度分析

    5、附录

    中位数一般两种定义:

    第一种:

    排序后数组的中间位置的值,如果数组的个数是偶数个,则返回排序后数组的第N/2个数。

    第一种(官方):

    排序后数组的中间位置的值,如果数组的个数是偶数个,则返回最中间两个数的平均数。

    例如:{ 7, 9, 4, 5}   第一种输出5;第二种输出6.0

    算法思想:大家应该都知道,快速排序每一躺都能定位一个数在这个数组的最终位置,所以可以利用此特性对数组中任意一个位置进行二分法定位。

        方法就是:一趟快排的partition结束之后,将此时定位的位置与欲定位位置进行比较,如果不等于,则将partition的区间折半,直到等于为止,返回这个位置的值

    Java代码实现

    因为第二种中位数定义需要定位两个位置,在第一种上扩展即可,所以先讨论第一种:

     1     public static int getMedian(int[] nums) {
     2         return partition(nums, 0, nums.length - 1);
     3     }
     4 
     5     private static int partition(int[] nums, int start, int end) {
     6         /***快排partition函数原代码——start***/
     7         int left = start;
     8         int right = end + 1;
     9 
    10         int point = nums[start];
    11         while (true) {
    12             while (left < right && nums[--right] >= point)
    13                 ;
    14             while (left < right && nums[++left] <= point)
    15                 ;
    16             if (left == right) {
    17                 break;
    18             } else {
    19                 int tmp = nums[left];
    20                 nums[left] = nums[right];
    21                 nums[right] = tmp;
    22             }
    23         }
    24         nums[start] = nums[left];
    25         nums[left] = point;
    26         /***快排partition函数原代码——end***/
    27         
    28         /***定位判断***/
    29         if (left == (nums.length - 1) / 2) {
    30             return nums[left];
    31         } else if (left > (nums.length - 1) / 2) {
    32             return partition(nums, start, left - 1);
    33         } else {
    34             return partition(nums, left + 1, end);
    35         }
    36     }

     其实就是在原来的partition结束后加了一个定位判断,此时left指向的就是已经本趟定位的那一个数,如果没有定位成功则将上下界调整折半。

    【注意】:“如果数组的个数是偶数个,则返回排序后数组的第N/2个数”这句话需要用 (nums.length - 1) / 2 来实现这句描述的下标,并满足奇数时取最中间下标的效果。

    时间复杂度分析

    由于此方法采用的也是递归,那么必定符合递归的复杂度通项表达式: T(n) = aT(n/b) + f(n) 

    其中a为每次递归会分成几个需要计算的下一层,(n/b)为下一层计算的元素个数,f(n)为本层的计算复杂度

    由于是折半查找,所以有:a=1、b=2(平均)、f(n)=n(每次的遍历比较交换)

    所以有

    T(n) = T(n/2) +n
            =  T(n/4) + n/2 +n
            ……
            = T(1) + 2 + …… + n/2 +n    // T(1)≈1 等比数列求和
            = (1 - n * 2)/(1 - 2)
            = 2n - 1

    所以最后平均时间复杂度为O(n)

    【最优情况下b=n复杂度O(n);

     最坏情况下b=n-1/n,也就是(n/b)=(n-1),此时复杂度为O(n²),请自行计算哈】

    附录——第二种求中位数的实现

    思路:第一种已经解决了定位一个数字,而第二种就是定位两个数字,由于定位一个数字的时候不能保证另一个数字已经排序好,所以还需重新调用方法

      那么就把方法中定位判断的部分单独移出来做一个getByQuickSort(int[] nums,int stop)

    java代码实现:

     1     public static double getByQuickSort(int[] nums, int stop) {
     2         if (stop < 0 || stop >= nums.length) {
     3             throw new IndexOutOfBoundsException();
     4         }
     5         
     6         int start = 0;
     7         int end = nums.length -1;
     8         int par = 0;
     9         
    10         while (start <= end) {
    11             par = partition(nums, start, end);
    12             if (par == stop) {
    13                 break;
    14             } else if (par > stop) {
    15                 end = par - 1;
    16             } else {
    17                 start = par + 1;
    18             }
    19         }
    20         return nums[par];
    21     }

    此处的partition(...)方法就是上一段代码中的partition方法中把 /***定位判断***/ 以下都去掉,然后加一个 return left; 即可。

    而找中位数就再写一个方法getMedian2(...)判断一下奇偶,再调用getByQuickSort(....)就可以了:

    1     public static double getMedian2(int[] nums) {
    2         if (nums.length % 2 == 1) {
    3             return getByQuickSort(nums, nums.length / 2);
    4         } else {
    5             return (getByQuickSort(nums, nums.length / 2 - 1) + 
    6                     getByQuickSort(nums, nums.length / 2)) / 2.0;
    7         }
    8     }
  • 相关阅读:
    屏蔽 优酷广告
    First Adventures in Google Closure -摘自网络
    Closure Compiler(封闭编辑器), Closure Inspector, Closure Templates, 封闭图书馆(Closure Library) Google- 摘自网络
    据说几年前,微信之父张小龙有一次入京到龙泉寺散心,心中关于微信的产品困惑久久不得解。无聊中,张小龙与寺中扫地僧攀谈起来,发现对方居然懂得技术和产品,深入聊天之后,张小龙震惊于对方的才学与见识,虚心请教,之后闭关七天回到深圳,微信终于大成。
    一个PHP书单 -摘自网络
    关于protel 99se 汉化后某些菜单消失的解决方法
    Protel画完原理图检查的时候出现了这些错误 #1 Error Multiple Net Identifiers
    protel99 se中出现许多Backup of 文件,修改过保存时,总会出现备份文件,怎么才能取消这一设置?
    protel 99se 加载库文件 files not recognised 解决办法-转
    Hosts 文件的作用
  • 原文地址:https://www.cnblogs.com/Xieyang-blog/p/8360948.html
Copyright © 2011-2022 走看看