文章目录
- 定义
- 实现 之 左右指针法
- 定义
- 图示
- 代码实现
- 实现结果
- 实现 之 填坑法
- 图示
- 实现
- 结果
- 性能
- 时间复杂度
- 空间复杂度
- 算法稳定性
定义
快速排序(Quick Sort)是一种有效的排序算法。虽然算法在最坏的情况下运行时间为O(n^2),但由于平均运行时间为O(nlogn),并且在内存使用、程序实现复杂性上表现优秀,尤其是对快速排序算法进行随机化的可能,使得快速排序在一般情况下是最实用的排序方法之一。
快速排序被认为是当前最优秀的内部排序方法。
实现 之 左右指针法
定义
有一个数组a[size],左指针left指向下标为0的位置,右指针right指向下标为size-1的位置,同时标志key=a[right];left找a[left]>key的元素的下标,right找a[right] < key的元素的下标。left先开始走,当遇到比key大的数时,停下来,再由right开始走,当遇到比key小的数时,停下来。将left和right分别指的数交换,前提是left<right时,如果left>=right就说明一次快排结束。再将left指向的值与key值交换即可。此时的数组呈现出左边的值都比key值小,右边的值都比key值大。一次快排将数组分为两个区间,我们再对每个区间进行上述的排序方式。直到每个小区间已不能再划分。
图示
如图所示,将一组数据进行快速排序,比key大的都换到key的右边,比key小的都换到key的左边。
然后再继续进行排序,划为子问题进行解决:
代码实现
class Sort:
def quick_sort(self, data, left, right):
if left < right:
k_idx = self.partation(data, left, right)
self.quick_sort(data, left, k_idx - 1)
self.quick_sort(data, k_idx + 1, right)
return data
def partation(self, data, low, high):
left = low
right = high
k = data[left]
while left < right:
while left < right and data[right] > k: # not >= otherwise IndexError: list index out of range
right -= 1
while left < right and data[left] <= k:
left += 1
if left < right:
data[left], data[right] = data[right], data[left]
data[low] = data[right]
data[right] = k
return right
实现结果
if __name__ == '__main__':
test = Sort()
L = [4,8,5,0,2,7,1,9,3,6]
res = test.quick_sort(L, 0, len(L)-1)
res # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
实现 之 填坑法
图示
上图中,演示了快速排序的处理过程:
初始状态为一组无序的数组:2、4、5、1、3。
经过以上操作步骤后,完成了第一次的排序,得到新的数组:1、2、5、4、3。
新的数组中,以2为分割点,左边都是比2小的数,右边都是比2大的数。
因为2已经在数组中找到了合适的位置,所以不用再动。
2左边的数组只有一个元素1,所以显然不用再排序,位置也被确定。(注:这种情况时,left指针和right指针显然是重合的。因此在代码中,我们可以通过设置判定条件left必须小于right,如果不满足,则不用排序了)。
而对于2右边的数组5、4、3,设置left指向5,right指向3,开始继续重复图中的一、二、三、四步骤,对新的数组进行排序。
实现
class Sort:
def quick_sort(self, data, left, right):
if left < right:
k_idx = self.partation(data, left, right)
self.quick_sort(data, left, k_idx - 1)
self.quick_sort(data, k_idx + 1, right)
return data
# 填坑法
def partation(self, data, low, high):
left = low
right = high
k = data[left]
while left < right:
while left < right and data[right] >= k:
right -= 1
if left < right:
data[left] = data[right]
while left < right and data[left] <= k:
left += 1
if left < right:
data[right] = data[left]
data[left] = k
return left
结果
if __name__ == '__main__':
test = Sort()
L = [4,8,5,0,2,7,1,9,3,6]
res = test.quick_sort(L, 0, len(L)-1)
res # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
性能
时间复杂度
当数据有序时,以第一个关键字为基准分为两个子序列,前一个子序列为空,此时执行效率最差。
而当数据随机分布时,以第一个关键字为基准分为两个子序列,两个子序列的元素个数接近相等,此时执行效率最好。
所以,数据越随机分布时,快速排序性能越好;数据越接近有序,快速排序性能越差。
空间复杂度
快速排序在每次分割的过程中,需要 1 个空间存储基准值。而快速排序的大概需要 Nlog2N次的分割处理,所以占用空间也是 O(Nlog2N) 个。
算法稳定性
在快速排序中,相等元素可能会因为分区而交换顺序,所以它是不稳定的算法。