Python:快速排序与归并排序

本文详细对比了快速排序和归并排序两种算法。在时间复杂度均为O(NlogN)的情况下,快速排序在空间上优于归并排序,但归并在稳定性与处理大规模数据方面更具优势。文章深入解析了两种算法的实现原理,包括快速排序的分治策略及归并排序的合并过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 快速排序和归并排序及其区别

首先在预期情况下的快速排序和归并排序时间复杂度都是O(NlogN), 在空间复杂度上,没使用临时栈的快速排序在空间上优于归并排序。

其次,在稳定性上来说,快速排序是不稳定的排序,归并排序与堆排序一样是稳定的排序,即排序后,比较值相同元素相对位置不变。如会议安排,通过比较会议优先级进行排序,若存在两个优先级相同的会议,但输入数据中前一个会议是下午开始,后一个是明天才开始,不稳定的排序可能会把明天才开始的会议放在下午的会议前面,在很多情况下,这种结果是不能被接受的。但若是对两个数组排序,那就不存在这个问题了。

再次,在并行的角度上说,二者都很容易实现分布式算法。归并排序将子序列分发下去后,需要等待其下属计算机的反馈,等得到有序子序列后,才能进行合并操作。相反快速排序却不一样,它只需告诉其下属计算机:将得到的子序列排序即可,并不需要各个计算机协同工作。

最后,值得一提的是,归并排序相比于快速排序,在面对大型数据集时显得更有效,因为归并排序并不需要一次装载全部数据(快速排序需要一次装入,选择分界值分割序列),而且快速排序需要不断切换子序列,这将增加内存分页,并大大减缓了算法的运行。

  • 快速排序

快速排序是一种不稳定的排序,与堆排序相比,它也有着O(NlogN)预期的性能,但最坏的情况时间复杂度会上升到O(N**2).

        算法思想
                  每次选择一个分界值divider,将小于divider的放置在序列左边,大于等于divider的放置在右边,然后再分别递归解决左边,和右边

        最佳的情况:每次选择的divider都处于序列中间,递归解决,我们可以发现递归树的每层的单个节点中的子序列长度在以2的指数幂递减,
                    最终形成的递归树的深度是logN,而每层总的元素数总是N,故每层需要处理的元素数为N-有限个divider数,N很大时可忽略不计,因此每层需处理的元素数粗略视为N,而递归树层数为logN,故最佳时间复杂度为O(NlogN)

        最坏的情况:每次选择的的divider都是极限值(最大或最小值),导致左边或右边为空。这样导致的
                    递归结果:若第一层(次)需要递归处理元素数为N,那么接下来调用递归处理左边和右边,需要处理的元素数为N-1,
                    再下一层是N-2,.....1 ,0,这样递归深度就达到了N的层次,整个递归树处理元素个数为N+N-1+...+1 =(N+1)N/2,
                    即时间复杂度达到O(N**2)

        合理的选择分界值:面对几十亿的数据,logN 大约是30,比N小得多,因此分界值divider需要合理的选择,否则很容易达到O(N**2),更重要的是会导致耗尽堆栈空间,导致程序崩溃。
                       *** 选择第一个元素作为divider
                            当数组本身有序时,或部分有序时,效果不佳
                       *** 在排序之前,将数组随机排列,随机排列使用线型同余发生器,时间复杂度为O(N)
                             当数组size很大时,会消耗一定时间
                       *** 比较首、中间、末三个元素,以他们的中位数作为分界值
                            避免了取极值的情况,但无法避免靠近极值
                       *** 随机待处理序列中的一个值作为分界值
                            由于随机性,很难遇到最坏情况

Python实现快速排序:

def quick_sort(array, start, end):
    # 没有或只有一个元素直接结束
    if start < 0 or start >= end:
        return

    # 选取第一个作为divider
    divider = array[start]
    low = start
    high = end
    # 把小值放在左边,大值放在右边,起始空位是divider==start,
    # end从右往左扫描,直到遇到low。碰到小于divider的值,将之放置到low,此时空位为end
    # low从左往右扫描,直到遇到end。碰到大于divider的值,将之放置到end的位置,此时空位为low
    # 扫描结束后,将divider放置到low位置,使之处于“中间”位置
    # 递归解决左边(start,low-1),(low+1,end)
    while low < high:
        # end从右往左扫描,直到遇到low。
        while array[high] >= divider:
            high = high - 1
            if low >= high:
                high = low
                break
        array[low] = array[high]

        # 扫描结束
        if low >= high:
            array[low] = divider
            break

        while array[low] < divider:
            low = low + 1
            if low >= high:
                low = high
                break
        array[high] = array[low]

        # 扫描结束
        if low >= high:
            array[low] = divider
            break

    # 递归解决左边
    quick_sort(array, start, low - 1)
    # 递归解决左边
    quick_sort(array, low + 1, end)
  •  归并排序

归并排序
        和快速排序一样,归并排序也是使用一个分而治之的策略。但归并排序和快速排序不同,归并排序选择中间元素为分解点,将序列分成两等份,然后分别递归处理左右两部分。

算法思想
        归并排序可分为两部分:
        第一部分为“分”:将序列分成两等份,并分别递归处理他们

 第二部分为“并“:当下层递归结束返回后,我们需要将下层递归处理好的子序列”并“起来,即将两个有序子序列 合并成一个有序序列

 合并完后将序列拷贝到原数组中,然后结束本层递归调用,返回到上一层递归调用,继续执行“并”,直到第一层递归调用也结束,算法结束

Python实现归并排序:

# array为原数组,array_tmp为临时数组,array_tmp由调用函数定义,降低程序内存压力
def merge_sort(array, array_tmp, start, end):
    # 递归出口
    if start == end:
        return

    mid_point = (start + end) // 2

    merge_sort(array, array_tmp, start, mid_point)
    merge_sort(array, array_tmp, mid_point + 1, end)

    # 合并子序列start~mid_point,mid_point+1~end
    left_index = start
    right_index = mid_point + 1
    tmp_index = start
    while left_index <= mid_point and right_index <= end:
        if array[left_index] <= array[right_index]:
            array_tmp[tmp_index] = array[left_index]
            left_index = left_index + 1
        else:
            array_tmp[tmp_index] = array[right_index]
            right_index = right_index + 1

        tmp_index = tmp_index + 1

    # 将左右剩余的元素加入array_tmp
    while left_index <= mid_point:
        array_tmp[tmp_index] = array[left_index]
        tmp_index = tmp_index + 1
        left_index = left_index + 1

    while right_index <= end:
        array_tmp[tmp_index] = array[right_index]
        tmp_index = tmp_index + 1
        right_index = right_index + 1

    # 将array_tmp中左右子序列合并成的新的有序序列拷贝回array,供上一层递归调用使用它(作为上一层的子序列)
    for i in range(start, end + 1):
        array[i] = array_tmp[i]


# 调用接口
def mergeSort(array):
    # array_tmp = array会导致二者指向同一对象,合并时出错
    array_tmp = [0] * len(array)
    merge_sort(array, array_tmp, 0, len(array) - 1)

 

 PS:代码clone地址:github.com:Rlyslata/DataStauct-And-Algorithm.git

路径:数组排序算法/MergeSort   数组排序算法/QuickSort

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值