算法一直是计算机学科中一个很是核心的内容,学习大黑书可让咱们年轻人获得充沛的力量(也就是少点头发),在程序的海洋里快乐徜徉。算法
排序算法是算法之中一个既基础又核心的内容,而快速排序则是比较排序中的佼佼者。今天咱们就一块儿来探究一下快速排序。segmentfault
快速排序是一个经典的分治算法,解决分治问题的三个步骤就是 分解、解决、合并。数组
拆开来看看快速排序的基本思想:dom
分解 :将输入数组A[l..r]划分红两个子数组的过程。选择一个p,使得A被划分红三部分,分别是A[l..p-1],A[p]和A[p+1..r]。而且使得A[l..p-1]中的元素都小于等于A[p],同时A[p]小于等于A[p+1..r]中的全部元素。性能
解决:递归调用快速排序,解决分解中划分生成的两个子序列的排序。学习
合并:由于子数组都是原址排序的,因此无需进行合并操做,数组A[p..r]已经有序。测试
算法导论书上给出了简单易懂的伪代码,我在这直接给出Python的实现代码ui
def Quick_Sort(A,p,r): if p<r: q=Partition(A,p,r) Quick_Sort(A,p,q-1) Quick_Sort(A,q,r) 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
这里看到数组的划分是直接选择了子数组的最后一个元素,那么当待排序列已经有序时,划分出的子序列便有一个序列是不含任何元素的,这使得排序的性能变差。为了改善这种状况,咱们能够选择引入一个随机量来破坏有序状态。spa
咱们能够经过在选择划分时随机选择一个主元来实现随机快速排序。仅需对上述代码作出小小的改动。code
def Quick_Sort_Random(A,p,r): if p<r: q=Partition1(A,p,r) Quick_Sort(A,p,q-1) Quick_Sort(A,q,r) def Partition1(A,p,r): k=random.randint(p,r) A[k],A[r]=A[r],A[k] return Partition(A,p,r)
是骡子是马咱们拉出来溜溜,我对两种快排的性能作了一个简单的测试。首先是必定数量的随机序列,运行的时间单位为秒,下表中的结果是经屡次运行所取得的平均值。
方法 | $10^3$ | $10^4$ | $10^5$ | $10^6$ | $10^7$ | 5*$10^7$ | $10^8$ |
---|---|---|---|---|---|---|---|
普通快排 | 0.00204557 | 0.02453995 | 0.32335813 | 4.83641084 | 63.91342704 | 456.20516078 | 1176.27041785 |
随机快排 | 0.00228848 | 0.03292949 | 0.39734049 | 5.41323487 | 66.26046769 | 451.38552999 | 1108.05737074 |
也可使用可视化的方法将上表变得更加清楚,普通排序在数据量较小时具备必定的性能优点,随机快排多是由于添加了随机选择这一项操做而影响了部分性能,可是随着数据量进一步增大,二者之间的性能会很是接近。
接下来是对有序序列进行测试,
方法 | $10^3$ | $10^4$ | $10^5$ | $10^6$ |
---|---|---|---|---|
普通快排 | 0.06262696 | / | / | / |
随机快排 | 0.03440228 | 0.45189877 | 7.28055120 | 95.54553382 |
普通快排在数据量很是小的时候就把栈给挤爆喽,从另外一侧面反映出随机快排的必要性,在处理比较极端也就是彻底有序的序列时具备较大的优点~