分析此排序算法的复杂性

时间:2016-05-07 05:02:47

标签: python algorithm sorting

我知道合并排序是对任意长度列表进行排序的最佳方法,但我想知道如何优化我当前的方法。

def sortList(l):
    '''
    Recursively sorts an arbitrary list, l, to increasing order.
    '''  
    #base case.
    if len(l) == 0 or len(l) == 1:
        return l

    oldNum = l[0]
    newL = sortList(l[1:]) #recursive call.

    #if oldNum is the smallest number, add it to the beginning.
    if oldNum <= newL[0]:
        return [oldNum] + newL

    #find where oldNum goes.
    for n in xrange(len(newL)):
        if oldNum >= newL[n]:
            try:
                if oldNum <= newL[n+1]:
                    return newL[:n+1] + [oldNum] + newL[n+1:]

            #if index n+1 is non-existant, oldNum must be the largest number.
            except IndexError:
                return newL + [oldNum]

这个功能的复杂性是什么?我在想O(n ^ 2),但我不确定。另外,有没有进一步优化这个程序? (除了放弃它并进行合并排序!)。

2 个答案:

答案 0 :(得分:0)

一个年轻的欧拉想出了一个似乎合适的公式。故事说,在小学的时候,他的老师非常疲惫,为了让班级忙碌一段时间,他们被告知将所有数字加起来为零到一百。年轻的欧拉回来了:

(n^2+n)/2

这适用于此,因为您的运行时间将与列表长度之前的所有数字的总和成比例,因为在最坏的情况下,您的函数将对已经排序的列表进行排序并将通过每次查找列表末尾的下一个元素的位置时,全长newL。

答案 1 :(得分:0)

我会优化您的代码。

  • 您执行了大量的列表副本:每次切片时,都会创建列表的新副本。这可以通过在函数声明中添加一个索引来避免,该索引指示数组中从哪里开始排序。
  • 您应该按照PEP 8进行命名:sort_list而不是sortList
  • 插入的代码有点奇怪;故意提出越界索引异常并不是正常的编程习惯。相反,只需将数值渗透到数组中,直到它位于正确的位置。

应用这些更改会产生以下代码:

def sort_list(l, i=0):
    if i == len(l): return
    sort_list(l, i+1)
    for j in xrange(i+1, len(l)):
        if l[j-1] <= l[j]: return
        l[j-1], l[j] = l[j], l[j-1]

现在对阵列进行就地排序,因此没有返回值。

以下是一些简单的测试:

cases = [
    [1, 2, 0, 3, 4, 5],
    [0, 1, 2, 3, 4, 5],
    [5, 4, 3, 2, 1, 1]
]

for c in cases:
    got = c[:]
    sort_list(got)
    if sorted(c) != got:
        print "sort_list(%s) = %s, want %s" % (c, got, sorted(c))

正如您所建议的那样,时间复杂度为O(n ^ 2),其中n是列表的长度。我的版本使用O(n)额外的内存,而你的版本使用O(n ^ 2),因为列表在每个阶段被复制的方式。

进一步改善内存使用的另一个步骤是消除递归。这是一个版本:

def sort_list(l):
    for i in xrange(len(l)-2, -1, -1):
        for j in xrange(i+1, len(l)):
            if l[j-1] <= l[j]: break
            l[j-1], l[j] = l[j], l[j-1]

这与递归版本的工作原理相同,但迭代执行;首先排序数组中的最后两个元素,然后排序最后三个,然后是最后四个,依此类推,直到整个数组都被排序。

这仍然具有运行时复杂度O(n ^ 2),但现在使用O(1)额外内存。此外,避免递归意味着您可以排序更长的列表,而无需在Python中达到众所周知的低递归限制。另一个好处是,在最好的情况下(当数组已经排序时),此代码现在是O(n)。