我的快速排序挂起大小为 2^13 的列表

My quicksort hangs for list of size 2^13

我在python中写了一个快速排序函数如下。

def quickSort1(A,p,q):
    if p < q:
        pivotIndex = partition(A,p,q)
        quickSort1(A,p,pivotIndex-1)
        quickSort1(A,pivotIndex+1,q)
def partition(A,first, last):
    pivot = A[first]
    tooBig = first+1
    tooSmall = last
    while True:
        while tooBig<=last and A[tooBig]<pivot:
            tooBig+=1
        while tooSmall>first and A[tooSmall]>pivot:
            tooSmall-=1
        if tooBig < tooSmall:
            temp = A[tooBig]
            A[tooBig] = A[tooSmall]
            A[tooSmall] = temp
        else:
            break
    A[first] = A[tooSmall]
    A[tooSmall] = pivot
    return tooSmall

我正在使用不同大小的列表(从 2 到 2^16)测试算法的执行时间。例如,

n = 2**13
s = [random.randint(-65536,65536) for i in range(n)]
#Begin Regular Quick Sort test with unsorted list
setup = '''
gc.enable()
from __main__ import quickSort1
from __main__ import n
from __main__ import s
            '''
 average = sum(timeit.Timer('A=s[:]; quickSort1(A,0,len(A)-1)',setup = setup).repeat(1,10))/10

我已经验证该算法适用于较小的尺寸,但一旦达到 2^13,程序就会挂起。我试过 sys.setrecursionlimit(2**30),但这并没有改变任何东西。我的算法有问题吗?

是的,你的逻辑有问题。如果 partition 收到一个子列表,其中 A[tooBig] == A[tooSmall],那么你的两个 while 循环条件都是 Falseif 交换相等的值。什么都没有改变,你陷入了无限循环。

我已经成功地用 212 重现了这个问题:当你的 **n 变得足够大以至于匹配端点很可能发生时,问题就出现了。

如果它有帮助,这里是我用来追踪问题的代码——它是你的,加上一些 print 插入和 indent 以帮助追踪通话深度。

indent = ""

def quickSort1(A,p,q):
    global indent
    print indent, "ENTER", p, q
    indent += "  "

    if p < q:
        pivotIndex = partition(A,p,q)
        quickSort1(A,p,pivotIndex-1)
        quickSort1(A,pivotIndex+1,q)
    indent = indent[2:]
    print indent, "LEAVE"


def partition(A,first, last):
    print indent, "  PART", first, last
    pivot = A[first]
    tooBig = first+1
    tooSmall = last
    while True:
        if abs(tooSmall-tooBig) < 10:
            print indent, tooSmall, tooBig, A[tooBig:tooSmall+1]
        while tooBig<=last and A[tooBig]<pivot:
            tooBig+=1
        while tooSmall>first and A[tooSmall]>pivot:
            tooSmall-=1
        if tooBig < tooSmall:
            temp = A[tooBig]
            A[tooBig] = A[tooSmall]
            A[tooSmall] = temp
        else:
            break
    A[first] = A[tooSmall]
    A[tooSmall] = pivot
    print indent, "  PART", tooSmall
    return tooSmall

最常见的循环是当列表下降到两个相等的值时。这是一个更长的:

2030 2022 [-32421, -32303, -32723, -32402, -32705, -32269, -32422, -32834, -32421]