插入排序
基本思想
插入排序是最基本的排序方式,下面给一个基本思想:
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。
伪代码
Insertion-Sort(A,n) //数组A有n个元素,对A进行排序
for j ← 2 to n
do key ← A[j]
i ← j-1
while i > 0 and A[i] > key
do A[i+1] ← A[i]
i ← i-1
A[i+1] ← key
为了更好的理解伪代码的过程,下面给个排序示意图:
假设现在有个数组A,它包含数据{8,2,4,9,3},那么插入排序的过程如下图所示:
算法分析
通过上图可以很清楚的看出插入排序的基本过程,下面我们分析一下插入排序的时间复杂度。
首先,考虑一下影响运行时间的因素(不考虑机器等硬件因素):
- 输入数据本身(有序输入,逆序输入)
输入规模(数据量大小,数据量越大,时间越长)
一般我们对一个算法的时间考虑,只关注其上界,也就是说,我们关注的是,最坏的情况下,算法的时间复杂度,即
T(n) = max time on any input of size n,T(n)为对任意大小n的数据输入所需要的最长时间。
对插入排序来说,最坏的情况,也就是逆序输入的情况,该情况下有:
也就是说,插入排序的时间复杂度是θ(n²)的,那么这个速度怎么评价呢?
- n较小的时候 → 快
- n很大的时候 → 不一定快
下面介绍一种时间复杂度相对较好的排序方法,归并排序。
归并排序
基本思想
归并排序的基本思想就是,将待排序的数据分为两个子部分,对两个子部分分别进行排序,最后合并两个子部分,得到已排序的数据。
算法流程
下面用算法流程来更好的展示一下这个方案的实现方式:
Merge-Sort A[1…n]
- if n = 1 ,完成;
- 递归排序:
A[1,…,[n/2]] 及A[[n/2]-1,…n]; - “合并”2个排好的序列;
算法分析
下面我们分析一下归并排序的时间复杂度,假设总时间需要T(n),我们来一步步计算该算法所需要的时间:
- 第一步,我们需要常数时间,即θ(1),其实可以忽略;
- 第二步,我们将一个大问题,分解成了两部分,每部分规模为之前的一半,于是我们可以假设第二步时间为 2T(n/2);
第三步,合并,为了更直观的理解合并时间,下面给出两个排好序的子序列,并展示一下合并过程。总体来看,合并就是从最小的两个数据开始比较,依次输出较小的数据,并移动指针,每次比较花费常数时间,对规模为n的数据来说,需要θ(n)的时间。
通过上面的分析,我们可以得到一个算法时间的表达式:
然后我们分析一下这个时间表达式怎么计算,这里需要用到递归求解的方法,下面采用递归树的方法,比较直观的来解释一下。首先,我们令θ(n) = cn(c为常数,c>0),则有:
下面将公示进行树展开:
我们可以将其展开至最后只剩下叶节点,可以算出树的高度为lgn,每层的时间为cn,最后一层为θ(n),将所有时间加起来,我们又可以得到公式:
从渐进的角度来看,nlgn是渐进的优于n²的,因为当n趋于无穷的时候,n²总会大于n。