稳定版快速排序算法

我们知道常规的快速排序算法是一个不稳定的算法,也就是两个相等的数排序之后的顺序可能和在原序列中的顺序不同。这是因为当选定一个枢轴(pivot),要把其他数分到小于pivot和大于pivot的两边的时候,不同实现的分法不一样。

下面我实现了一种稳定版快速排序算法,在Partition函数中保持了原序列中所有元素的相对顺序,只把pivot放到了它的正确位置。具体方法是三遍扫描原序列:1)第一遍先把小于pivot的元素按先后顺序放到tmp里,然后把pivot放到它的正确位置tmp[k];2)第二遍把大于pivot的元素按先后顺序追加在tmp里,这样除了pivot以前的其他元素,都保持了和原序列中一样的顺序;3)第三遍把tmp赋值回原数组A。

当排序算法稳定之后,就可以借此统计逆序数了,文件Q5.txt中共包含100000个不同的整数,每行一个数。我们可以使用稳定版快速排序算法对其排序,并统计出其中的逆序数个数。

具体的Python 3实现如下:

# -*- coding: utf-8 -*-
"""
Created on Tue Oct  6 00:21:37 2015
@author: bitjoy
"""
import time

inversions = 0

def Partition(A, p, r):
    global inversions
    tmp = [0] * (r-p+1)
    pivot = A[p]
    k = 0
    for i in range(p+1, r+1): # first
        if A[i] < pivot:
            tmp[k] = A[i]            
            inversions = inversions + i - k - p
            k = k + 1
    tmp[k] = pivot
    ans = k + p
    k = k + 1
    for i in range(p+1, r+1): # second
        if A[i] > pivot:
            tmp[k] = A[i]
            k = k + 1
    k = 0
    for i in range(p, r+1): # third
        A[i] = tmp[k]
        k = k + 1
    return ans

def QuickSortAndCount(A, p, r):
    if p < r:
        q = Partition(A, p, r)
        QuickSortAndCount(A, p, q-1)
        QuickSortAndCount(A, q + 1, r)

if __name__ == "__main__":
    Q5 = open('Q5.txt', encoding = 'utf-8')
    data = [ int(x) for x in Q5 ]
    Q5.close()
    start = time.clock()
    QuickSortAndCount(data, 0, len(data) -1 )
    end = time.clock()
    print("number of inversions:%d\ntime:%f s"%(inversions,end-start))

虽然这种快排的时间复杂度还是O(nlgn),但是在Partition函数中扫描了3次数组,并且借用了辅助数组tmp,不再是in-place排序算法,所以排序用时会比常规快排或者归并排序要慢。

Leave a Reply

Your email address will not be published. Required fields are marked *