算法【快速排序】
简介:
快速排序(Quicksort)是对"冒泡排序"的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法描述
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
Python代码:
方式一(普通版):
#基准值:默认情况下,使用序列中的第一个元素作为基准值也可是最后一个元素
#- 1.需要将比基准值小的元素放置在基准值左侧,比基准值大的元素放置在基准值右侧
#- 2.递归
def sort(alist,start,end):
low = start
high = end
#结束递归的条件
if low > high:
return
mid = alist[low] #基准值
while low < high:
while low < high:
#向左偏移high
if alist[high] > mid: #向左偏移high
high -= 1
else:
alist[low] = alist[high]
break
while low < high:
#向右偏移low
if alist[low] < mid: #向右偏移low
low += 1
else:
alist[high] = alist[low]
break
if low == high:
alist[low] = mid
#将sort递归作用到基数左侧子序列
sort(alist,start,low-1)
#sort递归作用在基数右侧子序列
sort(alist,high+1,end)
return alist
alist = [6,1,2,7,9,3,4,5,10,8]
print(sort(alist,0,len(alist)-1))
>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
方式二(精简版):
def quick_sort(lst):
if len(lst) < 2:
return lst
else:
piovot = lst[0]
less = [x for x in lst[1:] if x<=piovot]
greater = [x for x in lst[1:] if x>piovot]
return quick_sort(less)+[piovot]+quick_sort(greater)
array = [6,1,2,7,9,3,4,5,10,8]
print(quick_sort(array))
>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
性能分析
快速排序的一次划分算法从两头交替搜索,直到low和hight重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。
理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)。
最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。
为改善最坏情况下的时间性能,可采用其他方法选取中间数。通常采用“三者值取中”方法,即比较H->r[low].key、H->r[high].key与H->r[(10w+high)/2].key,取三者中关键字为中值的元素为中间数。
可以证明,快速排序的平均时间复杂度也是O(nlog2n)。因此,该排序方法被认为是目前最好的一种内部排序方法。
从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n))。