经过学习了上次的简单的概念,下面来看一个简单的插入排序算法
我在算法学习这里都要使用伪代码,而不是C语言,这样也能避免使用语言特性。或者使用伪代码更易懂。
下面关于分析算法的一些小知识
我们规定我们要使用的实现技术的模型是一种随机访问机(RAM)来执行我们的操作,在这个模型中指令一条接一条的执行,没有并发操作
RAM包含真实计算机中常见的指令:算术指令、数据移动指令、控制指令
这些操作所需的时间都是常量
值得注意的是指数运算,xy 运算需要若干个指令运算,所需时间不是个常量,但是许多计算机都有左移的操作,2x可以看做是左移右移得到的,所以是个常量级操作
在RAM的模型下,分析算法需要的数学工具包括组合学,概率论,代数技巧,以及识别一个公式最有意义的一项的能力。
分析插入排序算法的过程依赖于输入规模,显然排序三个数和1000个数是不一样的
输入规模的最佳概念依赖于所要研究的问题
一个算法在输入上的运行时间是指执行的基本操作或者步数。我们假定执行到第i步的时间为ci(常量),注释的时间为0,循环的判断也是需要时间的。
下面来看这个排序算法
插入排序A数组 代价 次数
for j=2 to A.length c1 n
key = A[j] c2 n-1
//insert A[j] into the sorted array A[1,j-1]. 0 n-1
i=j-1 c4 n-1
while i>0 and A[i]>key c5 t2+t3+....tn
A[i+1]=A[i] c6 (t2-1)+(t3-1)+....(tn-1)
i=i-1 C7 (t2-1)+(t3-1)+....(tn-1)
A[i+1]=key c8 n-1
将上面的写出具体的运行时间
T
(
n
)
=
c
1
n
+
c
2
(
n
−
1
)
+
c
4
(
n
−
1
)
+
c
5
∑
j
=
2
n
t
j
+
c
6
∑
j
=
2
n
(
t
j
−
1
)
+
c
7
∑
j
=
2
n
(
t
j
−
1
)
+
c
8
(
n
−
1
)
T_{(n)}=c_1n+c_2(n-1)+c_4(n-1)+c_5\sum_{j=2}^nt_j+c_6\sum_{j=2}^n(t_j-1)+c_7\sum_{j=2}^n(t_j-1)+c_8(n-1)
T(n)=c1n+c2(n−1)+c4(n−1)+c5j=2∑ntj+c6j=2∑n(tj−1)+c7j=2∑n(tj−1)+c8(n−1)
如果数组已经排好序,则是最佳情况
T
(
n
)
=
c
1
n
+
c
2
(
n
−
1
)
+
c
4
(
n
−
1
)
+
c
5
(
n
−
1
)
+
c
8
(
n
−
1
)
T_{(n)}=c_1n+c_2(n-1)+c_4(n-1)+c_5(n-1)+c_8(n-1)
T(n)=c1n+c2(n−1)+c4(n−1)+c5(n−1)+c8(n−1)
=
(
c
1
+
c
2
+
c
4
+
c
5
+
c
8
)
n
−
(
c
1
+
c
2
+
c
4
+
c
5
+
c
8
)
=(c_1+c_2+c_4+c_5+c_8)n-(c_1+c_2+c_4+c_5+c_8)
=(c1+c2+c4+c5+c8)n−(c1+c2+c4+c5+c8)
这时候可以表示成an+b ,因此此时它是个线性函数
倘若输入数组按照了反向排序,则导致最坏情况我们必须将每个元素A[j]与整个已排序子数组A[i,j-1]进行比较,所以对j=2,3,…,n有tj=j
∑
j
=
2
n
=
n
(
n
+
1
)
2
−
1
\sum_{j=2}^n=\frac{n(n+1)}2-1
j=2∑n=2n(n+1)−1
∑
j
=
2
n
(
j
−
1
)
=
n
(
n
−
1
)
2
\sum_{j=2}^n(j-1)=\frac{n(n-1)}2
j=2∑n(j−1)=2n(n−1)
那么运行时间为
T
(
n
)
=
c
1
n
+
c
2
(
n
−
1
)
+
c
4
(
n
−
1
)
+
c
5
(
n
(
n
−
1
)
2
−
1
)
+
c
6
(
n
(
n
−
1
)
2
)
+
c
7
(
n
(
n
−
1
)
2
)
+
c
8
(
n
−
1
)
T_{(n)}=c_1n+c_2(n-1)+c_4(n-1)+c_5(\frac{n(n-1)}2-1)+c_6(\frac{n(n-1)}2)+c_7(\frac{n(n-1)}2)+c_8(n-1)
T(n)=c1n+c2(n−1)+c4(n−1)+c5(2n(n−1)−1)+c6(2n(n−1))+c7(2n(n−1))+c8(n−1)
=
(
c
5
2
+
c
6
2
+
c
7
2
)
n
2
+
(
c
1
+
c
2
+
c
4
+
c
5
2
−
c
6
2
−
c
7
2
+
c
8
)
n
−
(
c
2
+
c
4
+
c
5
+
c
8
)
=(\frac{c_5}2+\frac{c_6}2+\frac{c_7}2)n^2+(c_1+c_2+c_4+\frac{c_5}2-\frac{c_6}2-\frac{c_7}2+c_8)n-(c_2+c_4+c_5+c_8)
=(2c5+2c6+2c7)n2+(c1+c2+c4+2c5−2c6−2c7+c8)n−(c2+c4+c5+c8)
这时候是个an2+bn+c其中a,b,c依赖于语句代价**ci**所以是二次函数。是平方性算法
这里我们分析了算法的最佳情况和最坏情况
然而我们经常来关注算法的最坏情况
以下有三点理由:
1.最坏情况给定了一个上界,知道了这个界,就能确保该算法不会需要更长时间,就不必要去猜测更复杂的情况并可以期望它不会变的更坏
2.对于某些算法,最坏情况经常出现,比如搜索算法
3.“平均情况”往往跟最坏情况一样差
我们同样关注的是运行时间的增长率和增长量级,所以我们通常只考虑最重要的项,因为n的值很大的时候,低阶项相对来说不重要,有时候甚至会忽略高阶项的常数系数。
所以此算法具有最坏运行时间 θ ( n 2 ) \theta(n^2) θ(n2)
蒟蒻的今天的总结到这里。