向插入排序添加反向特征的典型方法是什么?

时间:2013-03-19 20:21:42

标签: python algorithm

我写了以下插入排序算法

def insertionSort(L, reverse=False):
    for j in xrange(1,len(L)):
        valToInsert = L[j]
        i=j-1
        while i>=0 and L[i] > valToInsert:
            L[i+1] = L[i]
            i-=1
        L[i+1] = valToInsert
    return L

编辑:您需要做的就是更改最终的>到<让它反过来工作。

然而,大多数人在这些情况下做了什么?在两个if语句中写入算法两次,其中一个语句是&#和另一个<< s<代替?什么是"正确"通常处理这些变化较小的场景的方法,但它只是完全改变了循环/代码的性质?

我知道这个问题有点主观。

3 个答案:

答案 0 :(得分:9)

您可以为less-than运算符使用变量:

import operator
def insertionSort(L, reverse=False):       
    lt = operator.gt if reverse else operator.lt        
    for j in xrange(1,len(L)):
        valToInsert = L[j]
        i = j-1
        while 0 <= i and lt(valToInsert, L[i]):
            L[i+1] = L[i]
            i -= 1
        L[i+1] = valToInsert
    return L

答案 1 :(得分:1)

选项1:

def insertionSort(L, reverse=False):
    # loop is the same...
    if reverse:
        L.reverse()
    return L

选项2:

def insertionSort(L, reverse=False):
    if reverse:
        cmpfunc = lambda a, b: cmp(b, a)
    else:
        cmpfunc = cmp
    for j in xrange(1,len(L)):
        valToInsert = L[j]
        i=j-1
        while i>=0 and cmpfunc(L[i], valToInsert) > 0:
            L[i+1] = L[i]
            i-=1
        L[i+1] = valToInsert
    return L

答案 2 :(得分:0)

您可能会注意到sortedlist.sort以及执行任何潜在修饰处理的所有其他函数都有key参数,而那些专门执行排序的函数也有一个reverse参数。 (Sorting Mini-HOWTO涵盖了此内容。)

所以,你可以看看它们是如何实现的。不幸的是,在CPython中,所有这些东西都是用C语言实现的。它使用了一个名为“timsort”的自定义算法(在listsort.txt中描述)。但我认为可以解释这里的关键部分,因为它非常简单。 list.sort代码与sorted代码分开,并且它们都分布在一系列函数中。但是如果你只看一下顶级函数listsort,就可以看到它如何处理reverse标志:

1982     /* Reverse sort stability achieved by initially reversing the list,
1983     applying a stable forward sort, then reversing the final result. */
1984     if (reverse) {
1985         if (keys != NULL)
1986             reverse_slice(&keys[0], &keys[saved_ob_size]);
1987         reverse_slice(&saved_ob_item[0], &saved_ob_item[saved_ob_size]);
1988     }

为什么在开始和结束时反转列表?好吧,在列表几乎排序的情况下,许多排序算法 - 包括timsort和插入排序 - 将以正确的顺序开始比向后顺序更好。是的,它浪费了一个O(N)reverse调用,但你已经在做其中一个了 - 并且,因为任何排序算法至少是O(N log N),你的特别是O(N ^ N ^ 2),这不会使它在算法上更糟糕。当然对于小N,更好的排序,以及随机顺序的列表,这浪费的2N非常接近N log N,所以它可以在实践中有所作为。当N变大时,它会消失,但是如果你要分类数以百万计的小list,而不是几个大的,可能值得担心。

其次,请注意它通过创建反向切片进行反转。至少可能,这可以通过以相反的顺序引用具有list的原始__getitem__对象来优化,这意味着两个反转实际上是O(1)。最简单的方法是从字面上创建一个反向切片:lst[::-1]。不幸的是,这实际上创建了一个新的反转list,因此timsort包含自己的自定义反向切片对象。但是你可以通过创建ReversedList类来在Python中做同样的事情。

这在CPython中实际上可能不会更快,因为额外函数调用的成本可能高到足以淹没差异。但是你抱怨两个reverse调用的算法成本,这解决了问题,实际上与内置排序函数的方式相同。

你也可以看看PyPy是如何做到的。其list已在listobject.py中实施。它根据列表包含的内容委托给几个不同的策略类中的一个,但如果你查看所有策略(除了那些无关的策略),它们基本上都做同样的事情:sort列表,然后是reverse它。

所以,它对于CPython和PyPy来说已经足够了......对你来说这可能已经足够了。