算法:快速排序的神奇优化

我在研究三路快速排序时,想出来了一个点子,使得非三路快速排序也可以高效排序大量重复元素的数据。

原理是这样的:

单次排序开始前,先设定一个状态为真

遇到等于基准值的值时,如果状态为真则与小于基准值时做相同的操作,

且无论状态为何值都对其进行取反并赋回原值。

这样可以保证遇到了重复的数据时,重复数据被均匀的分到了基准值两侧,从而避免了大量重复数据带来的严重划分不均匀,并提高了速度,减少了递归深度。

我将其称作交替快排。

以下为 交替快排、三路快排、普通快排 的不同数据情况的速度对比:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5byg5aOu5b-XOTIz,size_16,color_FFFFFF,t_70,g_se,x_16

 

运行耗时对比

可以看出,三路快排在处理大量重复数据情况之外的速度有所下降,而交替快排则基本与普通快排相同。

交替快排对完全随机元素进行排序时虽然比普通快排慢一些但是也比三路快排更快。

以下贴出代码:

# -*- coding: utf-8; -*-


def quick_sort_three_ways(arr: list) -> None:
    """三路快排"""
    def f(l, r):
        while True:
            if l >= r:
                return
            if r - l < 10:
                # insertion sort
                for i in range(l + 1, r + 1):
                    j = i
                    num = arr[i]
                    while (k:=j-1) >= l and (n:=arr[k]) > num:
                        arr[j] = n
                        arr[k] = num
                        j = k
                return
            # quick sort
            # 调换 pivot
            mid = l + (r - l) // 2
            pivot = arr[mid]
            arr[r], arr[mid] = pivot, arr[r]
            i = l
            j = l
            k = r - 1
            while j <= k:
                num = arr[j]
                if num > pivot:
                    arr[j], arr[k] = arr[k], num
                    k -= 1
                elif num < pivot:
                    arr[j], arr[i] = arr[i], num
                    i += 1
                    j += 1
                else:
                    j += 1
            arr[j], arr[r] = pivot, arr[j]
            f(l, i - 1)
            l = j + 1  # 尾递归优化
    f(0, len(arr) - 1)


def quick_sort_alternate(arr):
    """快速排序, 相同元素交替优化"""
    def main(l, r):
        while True:
            if l >= r:
                return
            if r - l < 10:
                # if r-l < 10 then insert sort
                for i in range(l + 1, r + 1):
                    j = i
                    num = arr[i]
                    while (k:=j-1) >= l and (n:=arr[k]) > num:
                        arr[j] = n
                        arr[k] = num
                        j = k
                return
            mid = (r - l) // 2 + l
            arr[r], arr[mid] = arr[mid], arr[r]
            pivot = arr[r]
            j = l
            state = True
            for i in range(l, r):
                num = arr[i]
                if num < pivot:
                    arr[i], arr[j] = arr[j], num
                    j += 1
                elif num == pivot:
                    if state:
                        arr[i], arr[j] = arr[j], num
                        j += 1
                    state = not state
            arr[r], arr[j] = arr[j], pivot
            main(l, j - 1)
            # 尾递归优化
            l = j + 1
    main(0, len(arr) - 1)


def quick_sort(arr):
    """快速排序"""
    def main(l, r):
        while True:
            if l >= r:
                return
            if r - l < 10:
                # r-l < 10 then insert sort
                for i in range(l + 1, r + 1):
                    j = i
                    num = arr[i]
                    while (k:=j-1) >= l and (n:=arr[k]) > num:
                        arr[j] = n
                        arr[k] = num
                        j = k
                return
            mid = (r - l) // 2 + l
            pivot = arr[mid]
            arr[mid], arr[r] = arr[r], pivot
            j = l
            for i in range(l, r):
                num = arr[i]
                if num < pivot:
                    arr[i], arr[j] = arr[j], num
                    j += 1
            arr[r], arr[j] = arr[j], pivot
            main(l, j - 1)
            # 尾递归优化
            l = j + 1
    main(0, len(arr) - 1)


以下是用来生成随机数组的代码:

from random import random

def rand_arr(arr):
    for i in range(n):
        rand = int(random() * n)
        arr[i], arr[rand] = arr[rand], arr[i]


n = 100000

init_arr = [i for i in range(n)]

# 随机
random_arr = init_arr[:]
rand_arr(random_arr)

# 几乎有序
almost_sorted_arr = init_arr[:]
almost_sorted_arr[-1], almost_sorted_arr[n // 2] = \
        almost_sorted_arr[n // 2], almost_sorted_arr[-1]

# 倒序
reversed_arr = init_arr[::-1]

# 完全随机生成的数组而不是打乱的数组
random_build_arr = [int(random() * n) for i in range(n)]

# 大量重复
massive_repeat_arr = init_arr[:n // 10] * 10
rand_arr(massive_repeat_arr)

所以,似乎交替快排比三路快排更加实用呢!

如果有人有证据证明这种优化方法已经被发明过了,请联系我。

反正我是没搜到这种优化方法。三数取中我没用因为貌似我测的更慢。多线程优化没加。

插排阈值我设置的10。尾递归优化加了,取l与r中间的数做基准值优化加了,很好用。

有序检查优化没加,在我常用的场景不太划算。

不过在已经排好的重复元素多的数组中,再进行排序。

交替快排应该会比三路快排有着更多的交换次数,这倒是可以用交换前先检查值是否相等来解决,不过这样会略慢一些,所以根据需求看你需要减少交换还是提高其它情况下的速度。虽然除了大量重复依旧比三路快排快。

我在Bilibili同步发布了该文章

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值