算法导论——python实践(7.快速排序)

本文详细介绍了快速排序算法,包括其原理、随机化版本和性能分析。讨论了如何利用插入排序改善快速排序,特别是在数据几乎有序时的优势。同时,分析了Hoare划分的正确性和快速排序的栈深度问题,探讨了尾递归优化在快速排序中的应用。

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

1、快速排序的描述

快速排序是原址排序,不用新增某一序列用于存储在排序过程中的临时变量。原址排序就是在原来的数组上进行操作。

主要分为两步

分解:对于一个数组A[p.....r]排序,将其划分为两个子数组A[p.....q-1]和A[q+1.....r],使得A[p.....q-1]中的每一个元素都小于等于A[q],而A[q]也小于等于A[q+1.....r]中的每一个元素。此时我们就能将A[q]在正确排序后的正确下标索引解出来。对应下面程序的partition函数。

解决:通过递归调用快速排序,因为上一步已经把A[q]的正确位置找出,并且左边小于等于A[q],右边严格大于A[q],只要分别对左右两边再调用快速排序算法即可。

def partition(a,p,r):
    x=a[r]
    i=p-1
    for j in range(p,r):
        if a[j]<=x:
            i+=1
            a[i],a[j]=a[j],a[i]
    a[i+1],a[r]=a[r],a[i+1]
    return i+1
def quicksort(a,p,r):
    if p<r:
        q=partition(a,p,r)
        quicksort(a,p,q-1)
        quicksort(a,q+1,r)
quicksort(a,0,len(a)-1)

关于partition函数:其主要作用将数组分成两部分,并找到A[r]的正确位置,并且将数组分为三个区域(1)若p<=k<=i,a[k]<=x.(2)若i+1<=k<=j-1,a[k]>x.(3)若k==r,a[k]=x.

2、快速排序的随机化版本

import random
def partition(a,p,r):
    x=a[r]
    i=p-1
    for j in range(p,r):
        if a[j]<=x:
            i+=1
            a[i],a[j]=a[j],a[i]
    a[i+1],a[r]=a[r],a[i+1]
    return i+1
def randomized_partition(a,p,r):
    i=random.randint(p,r)
    a[i],a[r]=a[r],a[i]
    return partition(a,p,r)
def randomized_quicksort(a,p,r):
    if p<r:
        q=randomized_partition(a,p,r)
        randomized_quicksort(a,p,q-1)
        randomized_quicksort(a,q+1,r)

randomized_quicksort(a,0,len(a)-1)

为什么要引入随机化:由于可能初始的序列存在最坏情况的可能性,通过在算法中引入随机性,从而使得算法对于所有的输入都能获得较好的期望性能。

3、快速排序分析

最坏情况分析:运行时间\Theta (n^{2})

最好情况分析:运行时间\Omega (nlgn)

期望运行时间:

### 不同排序算法的性能与特点 #### 插入排序 插入排序是一种简单直观的排序方法,适用于较小规模的数据集。该算法通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。时间复杂度为 O(),但在几乎已经排序好的数组上表现良好。 ```python def insertion_sort(arr): for i in range(1, len(arr)): key = arr[i] j = i - 1 while j >=0 and key < arr[j]: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key ``` #### 归并排序 归并排序基于分治策略,将待排序列分成若干子列表直到每个子列表仅有一个元素;随后逐步合并这些子列表形成新的有序列表。此过程确保了整体有序性。平均情况下时间复杂度达到最优O(n log n)[^2]。 ```python def merge_sort(arr): if len(arr) > 1: mid = len(arr)//2 L = arr[:mid] R = arr[mid:] merge_sort(L) merge_sort(R) i = j = k = 0 while i < len(L) and j < len(R): if L[i] < R[j]: arr[k] = L[i] i+=1 else: arr[k] = R[j] j+=1 k+=1 while i < len(L): arr[k] = L[i] i += 1 k += 1 while j < len(R): arr[k] = R[j] j += 1 k += 1 ``` #### 快速排序 快速排序同样是采用分而治之的方法来实现高效排序。它选取一个'基准'(pivot), 将小于等于它的数放在左边,大于它的放右边,再分别对左右两部分递归处理。最坏情况下的时间复杂度退化到O(),但通常期望时间为O(nlogn) [^1]. ```python def quick_sort(arr): if len(arr)<=1: return arr else: pivot=arr[len(arr)//2] left=[x for x in arr if x<pivot ] middle=[x for x in arr if x==pivot] right=[x for x in arr if x>pivot] return quick_sort(left)+middle+quick_sort(right) ``` #### 排序稳定性与其他特性 除了效率考量外,还需关注排序算法是否保持原有相同数值项间的相对次序不变——即所谓的“稳定”。例如冒泡排序、插入排序都是稳定的;然而希尔排序、堆排序则不是。具体可参见下述表格总结[^3]: | 名字 | 时间复杂度 | 空间复杂度 | 是否稳定 | |--|----------| | 冒泡排序 | O() | O(1) | 是 | | 插入排序 | O() | O(1) | 是 | | 选择排序 | O() | O(1) | 否 | | 堆排序 | O(n log n) | O(1) | 否 | | 归并排序 | O(n log n) | O(n) | 是 | | 快速排序 | 平均O(n log n) | O(log n) | 否 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值