MIT 6.006 Lecture3 插入排序和归并排序
这是排序部分的第一讲,我们会先介绍一下排序问题及其应用,然后介绍插入排序和归并排序两种算法,并对比。最后用Python实现这两种算法。
排序问题及其应用
什么是排序问题
Input:
array A[1…n] of numbers.
Output:
permutation B[1…n] of A such that B[1] ≤ B[2] ≤ … ≤ B[n] .
e.g.
A = [7, 2, 5, 5, 9.6] → B = [2, 5, 5, 7, 9.6]
排序问题给定一个输入数组A,算法处理后输入一个有序数组B。
排序算法的应用
排序算法在很多方面都有应用,比如:
- 组织一些数据库,比如歌曲库、书单库、电话簿
- 让某些问题变得简单
比如找数组中值、二叉查找、找统计离群点等 - 一些不太明显的应用,比如数据压缩、电脑绘图等。
可以说排序算法是计算机科学一个很重要的基础,所以掌握好排序算法是很重要也很必要的。
两种排序算法
插入排序
插入排序可以说是最简单的一种排序算法,只需要四五行的Python代码就能实现。当然简单在很多时候意味着低效。
伪代码:
1 | 1. Insertion-Sort(A[],n) |
上述伪代码中第3行,将A[j]插入到已经排好序的子数组,这个过程中有一定的优化空间。因为子数组已经是排好序的,所以我们可以选择二分法插入。这样时间复杂度就变成了$\Theta(n)$变为$\Theta(log_2n)$。
即便是用二分查找法找到合适的位置,但是第4行伪代码中将A[j]插入到合适的位置依然要花费$\Theta(n)$。因为插入的过程是通过交换位置实现的。
所以不难看出插入排序要遍历2..n-1元素,将每个元素插入到合适位置的时间复杂度是$\Theta(n)$。所以插入排序的整体时间复杂度是$\Theta(n^2)$。
归并排序
归并排序算法是分治策略一种典型的应用。《算法导论》中给出了分治策略的三个步骤:
- 分解(Divide):
将原问题划分为一些子问题,这些子问题和原问题一样,只不过规模更下。 - 解决(Conquer):
递归地解决这些子问题,如果子问题的规模足够小,就停止递归,直接求解。 - 合并(Combine):
将子问题的解合并为原问题的解。
对应到归并排序算法
- 分解:
将针对整个数组的排序分为对左右两个子数组的排序。 - 解决
递归地解决这些子问题,当子问题的规模只剩一个元素是,不用排序就是有序的了。 - 合并:
归并排序算法的关键和难点也是在合并这一过程。合并做到将两个有序的数组原地合并为一个有序的数组。
归并排序算法伪代码如下:
1 | Merge-Sort A[1..n] |
分析:
可以列出归并排序的递归式:
$$
T(n) =
\begin{cases}
\Theta(1)\ \ 若\ n \le c\
2T(\frac{n}{2}) + \Theta(n)
\end{cases}
$$
利用master theory容易得出归并排序算法的时间复杂度是$\Theta(nlog_2n)$
Python实现
python本身已经内置了排序算法,而且时间复杂度不错。我们自己实现一遍主要是为了理解算法。
插入排序
1 | def InsertionSort(nums): |
归并排序
1 | def Merge(nums,s,m,e): |