最大时间复杂度为线性时间的中值查找算法
分析的时候真是层出不穷的新算法啊,不过也是增长知识,这个Linear Time Median Finding算法用于寻找随机序列的中间值,其最坏的时间复杂度也只有 O(n) ,可以说是非常棒了。
主要参考了 My Favorite Algorithm: Linear Time Median Finding ,文章比较了三种中间值查找算法,分别是:
- Sort Based Median
- Quickselect with Random Pivots
- Quickselect with median pivots
1.Sort Based Median
这种算法思路很简单,先对序列进行排序,再从中取中间值即可,取中间值是一个 O(1) 的时间复杂度,但排序的平均时间复杂度最快也是 O(nlog(n)) (快速排序/堆排序等等)。
2.Quickselect with Random Pivots
这个算法借鉴了快速排序的内核,快速排序是一个循环的过程,每次循环选好种子点后会将所有值划分为大于种子点和小于种子点的两个集合,我们可以通过这个划分进一步缩小寻找中间值的范围。
整个算法的思路是:
- 选择种子点,一般随机选择序列中的某个点;
- 比较序列中每个值与种子点的大小将序列分为大于种子点和小于种子点的两个集合;
- 观察两个集合的数量,比较其差异:
- 如果两个集合数量相等,那么种子点为中间值;
- 如果大于种子点集合数量小于小于种子点集合数量,那么种子点出现在小于种子点集合中;
- 反之类似;
作者证明了其平均时间复杂度为 O(n) ,但是,如果考虑最坏情况,即每次选取的种子点都为当时序列的最大值或最小值,那么,最终的时间复杂度是 O(n^{2}) ,这是需要避免的一种情况。
3.Quickselect with median pivots
这个算法借鉴了一篇1973年的论文,真是服,那时计算机都没啥好的都能写出现在还在用的算法。
算法设计的核心目的在于Deterministic O(n) ,即时间复杂度确定为 O(n) ,不应该会出现某些极端的情况时间复杂度为 O(n^{2}) ,算法改进的点就是选好种子点。
具体的看这篇文章或者看论文吧,我主要讲下我怎么理解的。
关键点我认为在下面这张图片里,其中种子点是中间这个值:
算法的步骤:
- 将数据按照5个一组的范围分为 \frac{n}{5} 组;
- 每组求中值;
- 求所有组的中值的中值,将该值作为种子点;
需要理解的就是为何这么选择有用,从这张图里可以看出这么多个信息:
- 每个列中从上到下元素逐渐变大;
- 中间行上从左到右元素逐渐变大;
所以,图片中间的值111可以比左上角8个元素都要大,也比右下角8个元素都要小,定性来说不会出现种子点是最小或者最大的情况,而且因为每次都有 \frac{3}{10} 的元素是在两极上的,所以最极端情况种子点也会将整个序列分为 \frac{3}{10} 和 \frac{7}{10} 大的两个子序列,所以这样子序列的时间复杂度就为 T(\frac{7}{10}n) 了,作者给出的迭代方程是:
T(n)=T(\frac{n}{5})+T(\frac{7}{10}n)+n
注: T(\frac{n}{5}) 表示寻找中间值的中间值,即红色椭圆那部分。但是感觉这个方程没有排序没每组的时间,其时间是线性的,每组排序时间为 5lg(5) ,那么 \frac{n}{5} 组时间为 nlg(5) 。
这个可以通过算法最终证明最后的时间复杂度为 O(n) 。文章最终也给出了试验结果,和理论完美符合。