尝试使用python实现快速排序算法

错误

def quick_sort(numbers,left,right):
    if right - left <= 1: 
        return numbers 

    temp = numbers[left] 
    i = left
    j = right - 1
    while i < j:
        while numbers[j] > temp and i < j:
            j = j - 1
        else:
            numbers[i] = numbers[j]

        while numbers[i] < temp and i < j #!!!
            i = i + 1
        else:
            numbers[j] = numbers[i]


    numbers[i] = temp

    quick_sort(numbers,left,i)
    quick_sort(numbers,i+1,right) 

    return numbers

test = [6,7,8,5,1,2,11,5,4]
print(quick_sort(test,0,len(test)))

错误在于加了#!!!的那一行,正确的做法应该是

while numbers[i] <= temp and i < j 

否则的话会出现从左侧数过来,遇到一个和temp相等的数,右侧数过来也遇到一个和temp相等的数
两者会不断的交换位置,即死循环
或者简单的点说就是循环条件未能完全覆盖所有可能情况,往往会导致失败
这里还收获了一点关于debug的想法

def quick_sort(numbers,left,right):
    print("left,right分别是%d,%d"%(left,right))
    if right - left <= 1: 

那时猜想是不是函数不断被调用,因此在这里写了一个print,但结果程序进入了死循环却始终没有输出。
这实际上是证明了不是由于函数不断被调用而造成的死循环,而是while语句没能结束。(之后动手草稿演算了一遍得到为什么会错误的结论)。反思时觉得如果当时自己立刻明白这里没有输出就意味着错误不在函数调用上,而是在于其他的循环部分,从而在分别每次在一个while下写上一句print(“在这个循环里我陷入死循环了”)想来能够更快帮助自己发现问题。

正确代码与理解

代码

def quick_sort(numbers,left,right):
    if right - left <= 1: 
        return numbers #注1

    temp = numbers[left] 
    i = left
    j = right - 1 #注2
    while i < j:
        while numbers[j] > temp and i < j:
            j = j - 1
        else:
            numbers[i] = numbers[j]

        while numbers[i] <= temp and i < j
            i = i + 1
        else:
            numbers[j] = numbers[i]


    numbers[i] = temp
    #注3

    quick_sort(numbers,left,i)
    quick_sort(numbers,i+1,right) 
    #注4

    return numbers

test = [6,7,8,5,1,2,11,5,4]
print(quick_sort(test,0,len(test)))

代码解读

注1:如果待排序的数组只有一个数了,那为什么还要继续排呢?

注2:把每个待排序数组的第一个数拿出来做基准数。同时用i和j来分夹出待排序数组。

注3:在遍历数组的过程中,完成以下任务:
先从右向左读取数,并和基准数做比较,如果没有小于基准数,就继续往前读取。如果大于基准数,则将该数的值赋予到基准数本来的位置。而**基准数此时已经被保留在temp中,相当于基准数已经被挖走,基准数原来的位置只是一个坑。而这个坑被后来的数填上之后,后来的数所在的位置也变成了一个坑。
这个想法来自于http://blog.csdn.net/morewindows/article/details/6684558
然后再从左向右读,找一个比基准数大的把那个位置的坑给填上。
如此反复直至i=j,此时数组遍历完成。
注意i,j循环结束时的i和j,至少有一个是表示被挖空了的位置的。所以最后如果是i循环结束到了i=j的状态,那numbers[j]是被挖空的位置,也就是numbers[i]是被挖空的位置。这个时候把基准数放进去。显然i之前的要么是本来就不比基准数大的要么是比基准数小的换过来的,当然就形成了较小组和较大组。

注4:
我一开始写的时候,一直犹豫两个问题,那就是要不要把较小组和较大组再做成两个数组。以及怎么在函数内部同时再处理这两个组。。
后来查阅了相关资料,意识到其实对待排序数组的确定,是通过left和right是两个参量夹出来的。并且处理较小组和较大组,实际上就是处理坐标0到坐标i-1的数组与坐标i+1到坐标len(numbers)-1的数组。
因而在函数内部调用两次函数递归即可。
在演算时还遇到一个我以为自己错的地方,那就是对较小组进行快速排序时,我传入的right还是原始数组的长度,也就是说j还是从原始数组最后一个往前找。实际上这是没有问题的。因为。。
较小组都比前一个基准数小了,难道还会比基准数后面的较大组的任何一个数大?你从较小组里选出来一个基准数,难道还能从后面找到一个更大的??
而较大组从坐标0开始处理较小组也是同理。

一个更加简洁的代码

def quick_sort(numbers,left,right):
    if right - left <= 0:
        return numbers

    tmp = numbers[left]
    i = left + 1
    j = right

    while i < j:
        while numbers[j] > tmp and i < j:
            j -= 1
        while numbers[i] <= tmp and i < j:
            i += 1
        numbers[i], numbers[j] = numbers[j], numbers[i]

    if numbers[i] > numbers[left]:
        numbers[i-1], numbers[left] = numbers[left], numbers[i-1]
    else:
        numbers[i], numbers[left] = numbers[left], numbers[i]

    quick_sort(numbers,left,i-1)
    quick_sort(numbers,i+1,right)

    return numbers

test = [6,7,8,5,1,2,11,5,4]
print(quick_sort(test, 0, len(test) - 1))
    原文作者:排序算法
    原文地址: https://blog.csdn.net/python613/article/details/54813592
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞