python 快速排序详述

 

快速排序是对“冒泡排序”的优化算法,都属于交换排序类

描述:它通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个过程递归进行,以此使得整个数据变成有序序列。

值得注意:目前C++,java,PHP,JS等语言的排序源码中引用排序算法就是快速排序的改进算法实现的。

性能方面:时间性能取决于快速排序递归的深度。

 

\时间复杂度空间复杂度
最坏情况 O(n^2) O(n)
最好情况 O(nlogn) O(logn)
平均情况 O(nlogn) O(logn)

由于关键字的比较和交换是跳跃的,所以是一种不稳定的排序。

 

实现代码示例:

# -*- coding:utf-8 -*-
__author__ = 'webber'

def quick_sort(lst):
    Qsort(lst, 0, len(lst)-1)

def Qsort(lst, low, high):
    if low >= high:
        return                              # 分段无记录或只有一个记录
    else:
        pivot = Partition(lst, low, high)    # 将lst一分为二,得出枢轴值pivot

        Qsort(lst, low, pivot-1)             # 对低子表递归排序
        Qsort(lst, pivot+1, high)            # 对高子表递归排序


"""
交换顺序表lst中子表的记录,使枢轴记录到位,并返回其所在位置
此时在它之前(后)的记录均不大(小)于它
"""
def Partition(lst, low, high):
    pivotkey = lst[low]             #用子表的第一个记录做枢轴记录
    while low < high:              #从表的两端交替想中间扫描
        while low < high and lst[high] >= pivotkey:
            high -= 1
        lst[low], lst[high] = lst[high], lst[low]   # 将比枢轴记录小的记录交换到低端

        while low < high and lst[low] <= pivotkey:
            low += 1
        lst[low], lst[high] = lst[high], lst[low]   # 将比枢轴记录大的记录交换到高端
    return low      # 返回枢轴所在位置

if __name__ == "__main__":
    lst = [3, 6, 1, 2, 82, 35, -10, 12, 55]
    quick_sort(lst)
    print lst

 

快速排序的优化

1、枢轴的选取

之前的快速排序代码示例中总是固定的选取第一个关键字作为枢轴值,即pivotkey = lst[low] ,这很不合理,第一个枢轴值应该尽量为序列的中间数。

改进:三数取中法,即取序列的左端、中间、右端三个数,经过比较取得三个数的中间值作为枢轴值。(后续有人提出对于更大的序列,可以取更多的数然后得出中间值,即九数取中法。)

实现示例:

def Partition(lst, low, high):

    middle = low + (high-low)/2
    if lst[low] > lst[high]:
        lst[low], lst[high] = lst[high], lst[low]
    if lst[middle] > lst[high]:
        lst[middle], lst[high] = lst[high], lst[middle]
    if lst[middle] > lst[low]:
        lst[middle],lst[low] = lst[low], lst[middle]        # 优化一:优化选取枢轴(三数取中法),保证lst[low]为三个数的中间值,然后赋值给pivotkey

    pivotkey = lst[low]             #用子表的第一个记录做枢轴记录

 

2、优化不必要的交换

 代码示例:

def Partition(lst, low, high):
    pivotkey = lst[low]             #用子表的第一个记录做枢轴记录
    tmp = pivotkey              #将枢轴关键字暂存到tmp中
    while low < high:              #从表的两端交替想中间扫描
        while low < high and lst[high] >= pivotkey:
            high -= 1
#        lst[low], lst[high] = lst[high], lst[low]   # 将比枢轴记录小的记录交换到低端
        lst[low] = lst[high]            # 采用替换而不是交换的方式
        while low < high and lst[low] <= pivotkey:
            low += 1
#        lst[low], lst[high] = lst[high], lst[low]   # 将比枢轴记录大的记录交换到高端
        lst[high] = lst[low]             # 采用替换而不是交换的方式
    lst[low] = tmp          #将枢轴值替换回lst[low]
    return low      # 返回枢轴所在位置

 

3、优化小数组时的排序方案

由于递归对于长度较小的序列反而会拖累算法的性能,所以需要与简单排序相结合,这里用简单排序中性能相对来说较好的插入排序。

代码示例如下:需要在Qsort中加一个阈值的判别,有人认为7更合适,也有人认为50更合适,这样,无论多长的排序序列,当被递归分成小于等于7的序列时,就用插入排序实现。

MAX_LENGTH_INSERT_SORT = 7
def Qsort(lst, low, high):
    if low >= high:
        return                              # 分段无记录或只有一个记录
    else:
        if (high-low) > MAX_LENGTH_INSERT_SORT:   #优化三:设定阈值,在阈值内则用插入排序
            pivot = Partition(lst, low, high)    # 将lst一分为二,得出枢轴值pivot

            Qsort(lst, low, pivot-1)             # 对低子表递归排序
            Qsort(lst, pivot+1, high)            # 对高子表递归排序
        else:
            insert_sort(lst)

 

4、对递归操作的优化

在Qsort中分别对高子表和低子表进行递归排序,当待排序序列划分极端不平衡时,会增加递归的深度,严重影响性能。所以可通过对Qsort实现“尾递归”来进行优化。

代码示例如下:

def Qsort(lst, low, high):
    if low >= high:
        return                              # 分段无记录或只有一个记录
    else:
        if (high-low) > MAX_LENGTH_INSERT_SORT:   #优化三:设定阈值,在阈值内则用插入排序
            while low < high:                     # 优化四:对递归操作的优化
                pivot = Partition(lst, low, high)    # 将lst一分为二,得出枢轴值pivot

                Qsort(lst, low, pivot-1)             # 对低子表递归排序
                # Qsort(lst, pivot+1, high)           # 对高子表递归排序
                low = pivot + 1                      # 尾递归
        else:
            insert_sort(lst)

这样的结果是相同的,但因采用迭代而不是递归的方法,可以缩减堆栈的深度,从而在待排序序列划分极端不平衡时大大提高了整体的性能。

 

关于性能测试

 

if __name__ == "__main__":
    start = time.clock()

    rand_lst = []
    for i in range(10000):
        rand_lst.append(round(random.random()*100, 2))
    quick_sort(rand_lst)

    end = time.clock()
    print "done  ", (end-start)

 

分别生成不同长度的随机数序列,1000、5000、10000等等,发现MAX_LENGTH_INSERT_SORT = 50时排序耗时更少,而且这里有个问题,可能是python的内部优化的更好一些,快速排序的优化一和优化二对于性能的提升帮助很大,排序10000的随机数只需要0.03秒,但是经过优化三和优化四之后,反而需要至少5秒的排序时间,这里没想通,所以觉得或许是python的优化机制不同,对于递归深度的支持更好一些。这里遗留一个问题。。。

 

posted @ 2016-12-06 14:40  webber_liu  阅读(253)  评论(0编辑  收藏  举报