首页 > 技术文章 > 选择中位数-线性时间算法

novice-dxx 2020-01-19 14:40 原文

  本章继续讲一些关于奇淫技巧(算法啦)的做法,对于一个无序数组,我们如何找到其中位数呢?

  首先回顾一下中位数的概念:是按顺序排列的一组数据中居于中间位置的数。

  1,当前的先决条件是无序数组,那根据原理可以很快想到一种解法,对数组进行遍历,每次找出其最大值、最小值,最终残留的一位或两位即为中位数(两位则取平均值),时间复杂度 O(N) * N;当然,一次遍历中我们可以同时获取到最大值和最小值,将遍历的次数降低一半到 O(N)*N/2,但同样难以改变其时间复杂度为 O(N2) 的事实(这里有想法的同学先不要着急否定,后面一步步迭代)。

  2,很明显,上述的方法无法达到我们的想要的一种状态,那反观概念,如果是排好序的数组,我们完全可以在一次计算中得到其中位数,那就可以对数组先进行一次快排,使其达到有序的状态再返回中位数,时间复杂度就是快排的复杂度 O(N * logN)

  3,换一种思路,我们知道数组的个数,那中位数无非就是整个数组中第 (n)/2(偶数则包含 n/2-1)大的数,所以我们也可以采用堆排的方案,找出第 i 位大的值即中位数,时间复杂度就是堆排的复杂度 O(N * logN)

  4,本章重点解法,我们假设每次可以将数组分成两个部分,时刻保证前半部分 A 的任何元素大于后半部分 B 的任何元素,那只需要知道数组的中位数是在前半部分还是后半部分既可递归查找,另一半便可以抛弃不需要再次遍历排序。大致思路便是这样,具体流程如下:

    1,按快速排序的第一部分流程,将第一个数据进行遍历,找出其最终位置 p,这是左边 A 均小于当前数值,后面 B 均大于当前数值

               2,如果 p - start + 1 == i,则即可返回当前数值

               3,如果 p - start + 1 < i,则中位数在 B 部分,递归修改 start, i;反之中位数在 A 部分,递归修改 end,i

  上述的方法其实最坏情况下(比如完全倒序或完全正序)的时间复杂度也会达到最差的 O(N2),所以这种方法的仅期望情况下(数次切割即可找到中位数),才可以线性时间内找到中位数,而且这种方法也会比传统的快排快一部分(因为丢弃了一部分)。

  ps:这一张本来是想写成私有日志,但感觉第四种算法也有一些巧妙的点,所以列出来,有兴趣的同学可以参考一下,另外《算法导论第三版》123p 提出一种最坏情况线性的选择算法,有兴趣的同学可以去研究一下。这一篇很短,仅个人喜好记录^_^

推荐阅读