概述
算法是计算机程序的一个基本的构建模块。评价算法质量的最基本的标准是正确性,另一个重要的标准是运行时间性能。当在一台真实、资源有限的计算机上运行一个算法的时候,经济性的考虑就有了用武之地,这样一个过程会消耗两种资源:处理时间和空间或内存。
统计指令
用于估算算法性能的另一种技术是统计对不同的问题规模所要执行的指令的数目。不管算法在什么平台上运行,这个统计数字对于算法所要执行的抽象的工作量给出了一个很好的预计。然而要记住,当统计指令的时候,所统计的是用于编写算法的较高级代码中的指令数目,而不是执行机器语言的程序中的指令数目。
当以这种方式分析算法的时候,你需要区分两种指令:
(1)不管问题规模多大,都执行相同次数的指令
(2)根据问题的规模,执行不同次数的指令
现在,我们先忽略第一类指令,因为它们的影响并不显著,第二类指令通常在循环或递归函数中可以找到。
复杂度分析
复杂度的阶
大O表示法
一个算法几乎不太可能只是严格执行等于n、n^2或k^n的那么多次的操作,算法通常在循环体内、循环体之前或之后还要执行其他的工作。例如,我们说算法执行2n+3或2n^2操作,可能会更加精确。
一个算法的总工作量,通常是多项式中的数项之和,当用多项式来表示工作量的时候,有一个项是主项,随着n变得越来越大,主项也变得很大,以至于你可以忽略其他的项所表示的工作量。例如,在多项式(1/2)n^2-(1/2)n中,我们主要关注平方项(1/2)n^2,实际上忽略掉线性项(1/2)n,还可以删除掉系数1/2,因为(1/2)n^2和n^2之间的差别不会随着n的增加而改变。这种类型的分析有时候叫做渐进分析(asymptotic analysis)。
搜索算法
搜索最小值
Python的min函数返回列表中的最小的项。为了研究这个算法的复杂度,我们开发了一个替代的版本,它返回了最小项的索引。这个算法假设列表不为空,并且其中的项的顺序是任意的,该算法首先将列表中的第1个位置当作最小项,然后,向右搜索以找到一个更小的项,如果找到了,将最小项的位置重新设置为当前位置,当这个算法到达列表末尾的时候,它就返回最小项的位置。
class indexOfMin():
def indexOfMin(self,lyst):
"""Return the index of the minmum item."""
minIndex = 0
currentIndex = 1
while currentIndex < len(lyst):
if lyst[currentIndex] < lyst[minIndex]:
minIndex = currentIndex
currentIndex += 1
return minIndex
if __name__ == "__main__":
a = indexOfMin()
lyst = [3,5,7,1,9,10]
print(a.indexOfMin(lyst))
对于大小为n的列表,该算法必须进行n-1次比较,因此,算法的复杂度为O(n)。
顺序搜索一个列表
Python的in运算符作为list类中名为__contains__的一个方法而实现。该方法在列表(任意排列的项)中搜索一个特定的项(叫做目标项)。在这样的一个列表中搜索一个目标项的唯一的方法是,从第1个位置的项开始,将其与目标项进行比较,如果这两个项相等,该方法返回一个True。否则,该方法移动到下一个位置并且将其项与目标项进行比较。如果该方法到达了最后一个位置,却仍然没有找到目标项,它就返回False。这种搜索叫做顺序搜索(sequential search)或线性搜索(linear search)。一个更为有用的顺序搜索函数,应该返回它所找到的目标项的索引,或者如果没有找到目标项的话,返回-1。
class Search():
def sequentialSearch(self,target,lyst):
"""Returns the position of the target item if found, or -1 otherwise."""
position = 0
while position < len(lyst):
if target == lyst[position]:
return position
position += 1
return -1
if __name__ == '__main__':
a = Search()
target = 3
lyst = [2,4,5,1,3,7]
print(a.sequentialSearch(target,lyst))
最好情况、最坏情况和平均情况的性能
有些算法的性能取决于所处数据的放置方式。顺序搜索算法在查找一个目标项的时候,在列表的开头处所做的工作比在列表的末尾所做的工作要少。对于这样的算法,我们可以确定其最好情况的性能、最坏情况的性能和平均情况的性能。通常一般考虑的是最坏情况的性能和平均情况的性能。
顺序搜索的分析要考虑如下3种情况:
(1)在最坏情况下,目标项位于列表的末尾,或者根本就不在列表之中。那么,算法必须访问每一个项,并且对大小为n的列表要执行n次迭代。因此,顺序搜索的最坏情况的复杂度为O(n)。
(2)在最好的情况下,算法只进行了1次迭代就在第1个位置找到目标项,复杂度为O(1)。
(3)要确定平均情况,把在每一个可能的位置找到目标项所需的迭代次数相加,并且总和除以n。因此,算法执行了(n+1)/2次迭代,对于很大的n,常数因子2的作用并不大,因此,平均情况下的复杂度仍然为O(n)。
显然,顺序搜索的最好情况的性能是很少见的,而平均情况和最坏情况的性能则基本相同。
有序列表的二叉搜索
对于那些没有按照特定的顺序排列的数据,顺序搜索是必要的。当搜索经过排序的数据的时候,可以使用二叉搜索(又称为二分搜索)。
现在来考虑Python实现二叉搜索的一个示例。首先,假设列表中的项都是按照升序排列的,搜索算法直接找到列表的中间位置,并且将该位置的项和目标项进行比较。如果它们是一致的,算法返回该位置。否则,如果目标项小于当前项,算法搜索列表中间位置以前的部分。反之,搜索中间以后的部分。
def binarySearch(target, lyst, profiler):
"""Returns the position of the target item if found,
or -1 otherwise."