快排介绍:
快排简单而言就是在数组中选取一个基准值。将小于基准值的放左边(或者放右边),大于基准值的放右边(或者放左边),分成两个数组。
再对得到的左右两个数组分别重复上述操作。这样若干轮下来我们会得到只有1个或0个元素的数组这种最基本情况。那不用排序直接返回即可。
举个例子:
【5,8,12,4,7,2,7】
我们计算的方法是这样的
1.选取基准值(可以按照一定的规则选取,或者随机选取,我们就直接选取第一个元素【5】吧)
2.切分得到两个数组(基准值单独提出来,我们就当不算数组)【4,2】(5)【8,12,7,7】
3.对左边数组重复1,2步骤得到【2】(4)【】
4,再对3得到的左边元素重复1,2步骤。因为3得到的两个数组只有1或0个元素,所以不用排序直接返回。得到【2,4】从而小于【5】的部分就排序完成了。退栈到第二步,执行右边的排序操作。同理就可以把大于5的部分也排序完成。
具体生成应该如下:
【5,8,12,4,7,2,7】
【4,2】(5)【8,12,7,7】
【2】(4)【】(5)【8,12,7,7】
【2】(4)【】(5)【7,7】(8)【12】
【2】(4)【】(5)【7】(7)【】(8)【12】
本质上快排是一种大事化小的分治思想。需要用到递归的方法。我认为这里有两种数组类型的快排。动态数组(非原地快排),以及非动态数组(原地快排)
一.动态数组快排
如果不需要原地排序,用动态数组排序其实比较好理解代码如下(python):
def quick_sort(self,num):
#left,right分别保存小于基准值和大于基准值的元素
left = []
right = []
#如果数组元素为0或1个,不用排序直接返回
if len(num) <= 1:
return num
#此处选取了第一个元素作为基准值,也可选随机下标
base = num[0]`
for n in num[1:]:
if n > base:
left.append(n)
else:
right.append(n)
return self.quick_sort(left) + [base] + self.quick_sort(right)
二.非动态数组快排(原地快排)
原地快排比上面的难理解一点。先上代码
class Solution(object):
#快排函数
def quick_sort(self,num,l,r):
#若排序的数组左边大于等于右边,说明只有一个或0个元素,自然不用处理
if l < r:
q = self.partition(num,l,r)
self.quick_sort(num,l,q-1)
self.quick_sort(num,q+1,r)
#函数名写为分区,实际上,排序在这里实现,上面函数只是实现了递归。这里实现了排序
def partition(self,num,l,r):
#这个index指向最后一个大于(或小于,取决于你排序的顺序)基准值的元素的前一个元素。一开始我们就假设没有,故等于r
index = r
#从右往左遍历,大于基准值的元素往右抛,index--
for i in range(r,l,-1):
if num[i] > num[l]:
#选取了最左边的元素为基准值。
num[i],num[index] = num[index],num[i]
index -= 1
#最后再把基准值移到当前index位置
num[index],num[l] = num[l],num[index]
return index
我简单再走一遍逻辑。还是【5,8,12,4,7,2,7】
实现逻辑如下
基准值为5,l = 0,r = 6,index = 6,从右往左遍历,大于基准值的往右抛
7 > 5,故7往右抛(和num[index]对调),index = 5
此时数组为【5,8,12,4,7,2,7】
2 < 5 不处理
此时数组为【5,8,12,4,7,2,7】
7 > 5 往右抛,num[5]和num[4]对调,index=4
此时数组为【5,8,12,4,2,7,7】
4<5不处理
12>5 往右抛.num[2]和num[[4]对调 index = 3
此时数组为【5,8,2,4,12,7,7】
8>5 往右抛num[1]和num[3]对调 index = 2
此时数组为【5,4,2,8,12,7,7】
遍历结束,将基准值移到index处 即num[l]和num[index]对调,此时数组为
【2,4,5,8,12,7,7】返回此时index值2.我们发现,下标大于2处所有元素大于基准值5,小于2处均小于等于基准值5.故一轮排序结束。再递归处理2左右的两个数组即可。