快速排序的三种实现方法——就学吧一学一个不吱声

本文详细介绍了快速排序算法的原理,包括其分治策略,以及三种实现方式:方法一通过新建数组递归调用,方法二使用双指针不新建数组,方法三在原数组上操作。分别分析了它们的空间复杂度和时间复杂度。
摘要由CSDN通过智能技术生成


前言

学无止境,笔勤不辍。最近笔者重温了几种排序算法,之后会一一详解,今天重点来讲述一下快速排序(非常好的排序利器),本文将会用三种不同的实现方法来详解它,希望給大家一些启示。


一、快速排序的原理

快速排序算法其实也带有一部分分治法的思想在中间,它的主要步骤(以升序为例):一次快速排序,确定一个哨兵(标志元素)使得它之前的所有元素都小于它,它之后的元素都大于它,然后使用递归调用,让所有元素都排好序,快速排序最优的时间复杂度是O(n*lgn)

二、实现方式

1.方法一新建一个数组,然后递归调用

代码如下(示例):
以下用python代码为例

def quicksort(A,start,end):
    if end - start==0:# 递归结束标志,说明分配下来的数组没有值,说明此部分已经整理完成
        return A
    else:
        A1 = []#存储比标兵小的元素
        A2 = []#存储比标兵大的元素
        A3 = []#存储等于标兵的元素
        for i in A:
            if i< A[end-1]:
                A1.append(i)
            elif i>A[end-1]:
                A2.append(i)
            elif i == A[end-1]:
                A3.append(i)
        return quicksort(A1,start,len(A1))+A3+quicksort(A2,0,len(A2))




if __name__=='__main__':
    A = [1,2,3,2,5,4,10,2,5,3,8,7,1]
    print(quicksort(A,0,len(A)))

结果如下:
在这里插入图片描述
当然,这中解决方法,需要创建新的数组它所需要的空间复杂度很大大致是O(N^2) 时间复杂度大约是O(N*lgN)

2.方法二双指针,然后递归调用

代码如下(示例):

def quicksort(A,start,end):
    i = start
    j = end-1
    print(i)
    print(end)
    if end-start==1 or end-start==0:#由于某些情况下
    #end和start可能是相同的 所以用or去判断
        return A
    key =A[j]
    while i<j:
        if A[i]<key:#若i所指元素小于标兵则i++,判断下一个
            i+=1
            continue
        else:#否则与j所指元素交换,由于此时i所指元素一定大于等于标兵
        #因此此时j所指元素一定可以放在标兵值的后面所以 j--
            k = A[j]
            A[j] = A[i]
            A[i] = k
            j-=1
        if A[j]>key:
            j-=1
            continue
        else:
            k =A[j]
            A[j]=A[i]
            A[i]=k
            i+=1
    return quicksort(A[start:i],start,i)+[A[i]]+quicksort(A[i+1:end],0,len(A[i+1:end]))


if __name__=='__main__':
    A = [1,2,3,5,2,4]
    print(quicksort(A,0,len(A)))

结果如图:
在这里插入图片描述
PYTHON的传参有一点点恶心。然后这个折磨了笔者好久,希望能大佬给点建议!然后此时空间复杂度是O(n) 如果原本数组空间不计,那就是O(1)了
(在解释F3时,我好像明白了这个的局限性。。到时候修改!)

3.利用原数组,创造一个新的空间

def quicksort(A, start, end):
    i = -1#指向最后一个小于等于标兵的位置下标(0-0、i)是小于等于标兵的部分
    j = 0#指向最新一个大于标兵的位置(i+1,j)是大于标兵的部分
    #等于标兵的部分其实放哪都可以
    key = A[end ]
    while j <= end - 1:
        if A[j] > key:#原地开辟一个大于标兵值的一个空间
            j += 1
        else:#如果j指向的值小于等于标兵,则可以交换i+1和j指向的元素
        #因为,i指向的是最后一个小于等于标兵的值,i+1指向的是大于标兵的值
        #由此交换i+1和j不会该变快排的两个部分。
            k = A[j]
            i += 1
            A[j] = A[i]
            A[i] = k
            j += 1
    A.insert(i+1 , key)#在相应的位置插入标兵值

    A.pop(end+1)#因为list长度+1,要把末尾重复的标兵值删除
    return i + 1


if __name__ == '__main__':
    A = [1, 2, 3, 5, 2, 4, 5, 324, 123, 1,3]


    def find(A, start, end):
        if start < end:
            num = quicksort(A, start, end)
            find(A, start, num - 1)
            find(A, num + 1, end)

    find(A, 0, len(A)-1)
    print(A)

结果如图:
在这里插入图片描述
其实和方法2差不多,大家可以比较一下,选择适合自己的双指针方式
空间复杂度是O(1) 时间复杂度最优O(n*lgn)

下标和个数不是一一对应而是数组元素个数=下标+1这一点很重要!

流程图后续也会放上!


总结

以上就是今天要讲的内容,其实研究算法是一件有趣又痛苦的事情,希望大家能通过笔者的分享收获一些有用的东西,也可以订阅一下专栏!哈哈,期待下一次的更新…

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值