在归并排序中对小数组采用插入排序

摘要: 使递归的叶子变粗

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】

我的网站:潮汐朝夕的生活实验室

我的公众号:算法题刷刷

我的知乎:潮汐朝夕

我的github:FennelDumplings

我的leetcode:FennelDumplings


在文章 分治算法的设计与分析-归并排序 中,我们了解了分治算法的设计和分析方法,并且得出了归并排序算法的最坏情况运行时间为 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)。

在文章 基于随机访问机模型分析算法 中,我们了解了算法分析的方法论,并且以插入排序为例,得到了插入排序最坏情况运行时间为 Θ ( n 2 ) \Theta(n^{2}) Θ(n2)。

虽然开起来好像归并排序比插入排序的运行时间要好,但插入排序中的常量因子可能使得它在 n 较小时,在许多机器上实际运行得更快。因此归并排序中,当子问题规模足够小时,采用插入排序使得递归的叶变粗是有意义的。

本文我们来分析一下,子问题规模多小的时候采用插入排序,可以取得收益。

问题描述

考虑长度为 n 的数组,使用插入排序来排序长度为 k k k 的 n k \frac{n}{k} kn 个子表,然后用归并排序的合并机制来合并这些子表。

分析这个算法的运行时间,与归并排序进行对比,并研究实践中选择 k 的策略。

排序 n / k n/k n/k 个子表的运行时间

插入排序一个长为 k k k 的数组,最坏情况的运行时间为 Θ ( k 2 ) \Theta(k^{2}) Θ(k2),于是排序 n k \frac{n}{k} kn 个子表的运行时间为:

Θ ( k 2 ) × n k = Θ ( n k ) \Theta(k^{2}) \times \frac{n}{k} = \Theta(nk) Θ(k2)×kn=Θ(nk)

合并 n / k n/k n/k 个子表的运行时间

合并 K K K 个已排序的数组(链表),每个已排序数组长度为 N N N,合并后总长度为 K N KN KN。在 力扣23-合并K个升序链表 中我们解决过这个问题,有堆和分治两种方法,运行时间一样。分析过程如下:

如果用堆的方法,堆的容量为 K K K,因此堆的插入删除运行时间 Θ ( log ⁡ K ) \Theta(\log K) Θ(logK),而 K K K 个已排序数组的总长度为 K N KN KN,因此总的运行时间为: Θ ( K N log ⁡ K ) \Theta(KN\log K) Θ(KNlogK)。

如果用分治法,过程如下:

  • 分解:将 K K K 个已排序数组分为两份,每份 K / 2 K / 2 K/2 个
  • 解决:如果 K = 1 K = 1 K=1,可以直接解决,运行时间 Θ ( 1 ) \Theta(1) Θ(1)
  • 合并:将两个有序数组合并为 1 个

按照以上流程,每一轮合k并 K 2 \frac{K}{2} 2K 组链表,每一组 2 个,因此每组运行时间为 Θ ( 2 N ) \Theta(2N) Θ(2N);每二轮合并 k 4 \frac{k}{4} 4k 组链表,每一组 4 个,因此每组运行时间为 Θ ( 4 N ) \Theta(4N) Θ(4N),以此类推,总运行时间为 Θ ( ∑ i = 1 log ⁡ K K 2 i × 2 i N ) = Θ ( K N log ⁡ K ) \Theta(\sum\limits_{i=1}\limits^{\log K}\frac{K}{2^{i}}\times 2^{i}N) = \Theta(KN\log K) Θ(i=1∑logK2iK×2iN)=Θ(KNlogK)。

这里我们是要合并 n k \frac{n}{k} kn 个已排序数组,每个的长度为 k k k,合并后总长度 n n n,因此合并的运行时间为 Θ ( n log ⁡ n k ) \Theta(n\log \frac{n}{k}) Θ(nlogkn)。

整体的运行时间

在归并排序中,对小数组用插入排序,分为排序 n / k n/k n/k 个子表,合并 n / k n/k n/k 个子表两步,前面分别分析了这两步的运行时间,因此总运行时间为:

Θ ( n k ) + Θ ( n log ⁡ n k ) = Θ ( n k + n log ⁡ n k ) = Θ ( n log ⁡ n + n k − n log ⁡ k ) = Θ ( n log ⁡ n + n ( k − log ⁡ k ) ) \begin{align} \Theta(nk) + \Theta(n\log\frac{n}{k}) &= \Theta(nk + n\log\frac{n}{k}) \\ &= \Theta(n\log n + nk - n\log k) \\ &= \Theta(n\log n + n(k - \log k)) \\ \end{align} Θ(nk)+Θ(nlogkn)=Θ(nk+nlogkn)=Θ(nlogn+nk−nlogk)=Θ(nlogn+n(k−logk))

可以看到,修改过的算法的运行时间最低也是 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn),与归并排序一样。如果 k k k 取的不好,比如 k > log ⁡ n k > \log n k>logn,修改过的算法的运行时间还会大于归并排序的 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)。

实践中如何选择 k

根据具体的计算环境,选择使得插入排序比归并排序快的最大的 k k k,但最大不要大于 log ⁡ n \log n logn。

相关推荐
游是水里的游1 分钟前
【算法day19】回溯:分割与子集问题
算法
不想当程序猿_2 分钟前
【蓝桥杯每日一题】分糖果——DFS
c++·算法·蓝桥杯·深度优先
南城花随雪。21 分钟前
单片机:实现FFT快速傅里叶变换算法(附带源码)
单片机·嵌入式硬件·算法
dundunmm37 分钟前
机器学习之scikit-learn(简称 sklearn)
python·算法·机器学习·scikit-learn·sklearn·分类算法
古希腊掌管学习的神37 分钟前
[机器学习]sklearn入门指南(1)
人工智能·python·算法·机器学习·sklearn
波音彬要多做38 分钟前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
程序员老冯头3 小时前
第十五章 C++ 数组
开发语言·c++·算法
AC使者7 小时前
5820 丰富的周日生活
数据结构·算法
cwj&xyp8 小时前
Python(二)str、list、tuple、dict、set
前端·python·算法
xiaoshiguang312 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode