其实本篇文章重在分析时间复杂度。
先看一个简单的侏儒排序法,其中只有一个简单的while循环和一个索引范围为0到len(seq)-1的索引变量。
# 排序算法之:侏儒排序法
def gnomesort(seq):
i = 0
while i < len(seq):
if i == 0 or seq[i-1] <=seq[i]:
i += 1
else:
seq[i], seq[i-1] = seq[i-1], seq[i]
i -=1
看到这个while和索引范围,很让人容易认为他是一个线性时间的算法,但是不要忘记最后一行有i -= 1语句。
他会从左边开始扫描,直到找到一个seq[i-1]大于seq[i]的位置i,这是两个值的顺序就错了,于是else的部分开始生效,然后交换位置之后,继续向上扫描。
最好的情况是目标序列已经排好序了。这时排序函数只需要将整个序列扫描一遍即可,并不会发现任何错位,然后便停止了。运算复杂度应为Θ(n).
最坏的情况就是查找并移动这些错位元素的开销与其位置形成了正比关系。最糟糕的运行时间应该是1+2+···+n-1,也就是Θ(n²)。
最坏情况也是有可能发生的,于是侏儒排序的运行时间应为Ω(n)与O(n²)来表示,他们分别代表了最好的和最坏的情况。
下面来看一个很著名的归并排序法:
# 排序算法之:归并排序法
def mergesort(seq):
mid = len(seq)/2
lft, rgt = seq[:mid], seq[mid:]
if len(lft) > 1:
lft = mergesort(lft)
rgt = mergesort(rgt)
res = []
while lft and rgt:
if lft[-1] >= rgt[-1]:
res.append(lft.pop())
else:
res.append(rgt.pop())
res.reverse()
return (lft or rgt) + res
归并排序要比侏儒排序稍微复杂一些。尽管你可能不了解算法工作的前提下,仍然是可以进行分析的。
整体结构为:其输入(seq)规模为n,有两重递归调用,每次子问题的规模为n/2(尽可能让其接近整数的规模)。除此之外最后的res.reverse()也包含了部分操作。
这个如果进行推倒,得出递归式应该是T(n)=2T(n/2)+Θ(n),所以归并排序的运行时间为Θ(nlgn)。
无论输入情况如何总是这个结果。
当然这个结果你不能说都用归并排序解决生活中的问题。遇到几乎是已经排好序的数据,我们可以选用侏儒排序,但是在不确定的情况下,我们仍需要选择归并排序来解决问题。
谢谢各位关注.天气寒冷,大家注意保暖。