本文总结自:慕课网  (作者:liuyubobobo )

1. 快速排序

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

快速排序算法的思路是每次选择一个元素作为标的点(pivot),将整个数组的所有元素分为小于这个元素和大于这个元素两部分,之后再递归地对左右两部分分别进行这个过程,直至整个数组有序。

快速排序和三路快排之Partition 随笔 第1张

显然,在这个过程中,“选择一个元素作为标的点(pivot),将整个数组的所有元素分为小于这个元素和大于这个元素两部分”这步操作是最重要的,也是快速排序的核心所在。这个步骤被称为 Partition。通常将数组第一个元素作为pivot

Partition算法实现步骤:

首先记录首位置元素,然后在后续依次便利过程中,希望总有以下过程,即<v和>v两部分。
快速排序和三路快排之Partition 随笔 第2张

我们利用j来代表分界点,即[l+1,j]区间和[j+1,i-1]区间分别满足条件。i为我们当前访问的元素。

快速排序和三路快排之Partition 随笔 第3张

那如何决定当前i的元素呢?分两种情况,即e>v或<=v。可见,如果e>v,那我们直接合并e在紫色区间,也就是说i直接右移。如果e<v呢?略复杂一点:我们要将e放到橙色的部分。下面是解释:将e与j后面的元素做交换,那e就加入了橙色,i的位置就仍是大于v的数。下面j右移,i继续右移即可。

快速排序和三路快排之Partition 随笔 第4张

最终情况如下:

快速排序和三路快排之Partition 随笔 第5张

那么我们只需要将l和j位置交换,就满足了该元素左面都小于它,右面都大于他。

快速排序和三路快排之Partition 随笔 第6张

 基于以上,可以写出partition算法以及递归快速排序。

 

 1 def paitition(arr): # pritition  2     if not arr: return None  3     v = arr[0]  4     j = 0               # [l+1,j]>v [j+1,i)>v
 5     for i in range(len(arr)):  6         if arr[i]<v: # 当前元素小于v则交换  7             arr[j+1],arr[i] = arr[i],arr[j+1]  8             j=j+1
 9     arr[0],arr[j] = arr[j],arr[0] # 最后将该首位元素pivot交换到合理位置 10     return j,arr 11 
12 
13 def quicksort(arr): # 快速排序 14     if len(arr)<=1:return arr # 如果小于2个元素则直接返回 15     
16     index,arr = paitition(arr) # 找到pivot元素和位置 17     arr[0:index] = quicksort(arr[0:index]) # 排序pivot左半部分 18     arr[index+1:len(arr)] = quicksort(arr[index+1:len(arr)]) # 排序pivot元素右部分 19     
20     return arr 21 
22 
23 print(quicksort([0,1,2,0,3,2,4,2,402,0]))   # 测试快排

 

如何写出这段简介的partition代码?

在写代码之前,要考虑清楚具体边界情况: [l+1,j]>v,  [j+1,i)>v。然后根据一般情况写代码即可。每次定义一个变量,就要考虑清楚其具体作用,例如变量j就负责使得[l+1,j]满足元素都小于v。而i是当前情况,[j+1,i)是左闭又开区间。

 

快速排序的问题

当数组元素有非常多的重复元素时,快速排序会沦为非常可怕的O(n^3)复杂度。前面没有提到=v的情况,都是<v的放左边,>v的放右边。可是当有大量重复元素时,快速排序就会变得非常不平衡,<=v的部分很多,或者>=v的部分很多。如下图:

 

快速排序和三路快排之Partition 随笔 第7张

改进:三路快排。将=v的部分单独拿出来,将<v和>v的部分分别置于数组两端。

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄