思路:
快速排序的本质就是选取一个基准数,通过一次快排把基准数大的都放在基准数的左边,把比基准数小的放在基准数的右边,这样就找到了基准数在数组中的正确位置。
然后可以用递归的方式分别对前半部分和后半部分排序,最终实现整体数组有序。
快排是不是稳定排序?
所谓排序的稳定性,就是指在排序过程中,在对某关键字排序后会不会改变其他关键字的顺序。快排会改变,所以不是稳定排序。
代码:
常规代码1:
def AdjustPartition(list1,low,high):
if low < high:
left = low
right = high
key = list1[left]
while left < right:
while left < right and list1[right] >= key:
right = right - 1
list1[left] = list1[right]
while left < right and list1[left] <= key:
left = left + 1
list1[right] = list1[left]
list1[left] = key
AdjustPartition(list1,low,left-1)
AdjustPartition(list1,left+1,high)
return list1
常规代码2:
def quick_sort(arr):
if len(arr) < 2:
return arr
key = arr[0] # 以第一个关键字作为基准数
left, right = [], []
arr.remove(key)
for item in arr:
if item >= key:
right.append(item)
else:
left.append(item)
return quick_sort(left) + [key] + quick_sort(right)
python一行代码实现快排:
def quick_sort(a):
if len(a) > 1:
return quick_sort([i for i in a[1:] if i <= a[0]]) + [a[0]] + quick_sort([i for i in a[1:] if i > a[0]])
else:
return a
转化为一行:
quick_sort = lambda a:a if len(a) <= 1 else quick_sort([i for i in a[1:] if i <= a[0]]) + \
[a[0]] + quick_sort([i for i in a[1:] if i > a[0]])
快排优化
如果数组元素是随机的,那么程序的运行时间不会有太大的波动。如果数组元素已经基本有序时,此时的固定基准划分就容易产生最坏的情况,即快速排序变成冒泡排序,时间复杂度为O(
n
2
n^{2}
n2)。
eg: 序列1 2 3 6 12 18,如果用固定基准进行快排:(【】内为基准数)
第一趟:【1 】2 3 6 12 18
第二趟:1【 2】 3 6 12 18
第三趟:1 2 【3】 6 12 18
第四趟:1 2 3 【6】 12 18
第四趟:1 2 3 6 【12】 18
快排基准的选择:
(1)固定基准
比如每次以第一个关键字作为基准数。可能会退化为冒泡排序。
(2)随机基准
可以利用随机函数随机生成在[low,high]范围内的数作为基准数的下标。
(3)三数取中
随机基准选取的随机性,因此即使是同一个数组每次效率也是不一样的,此时可以选择三数取中的方法选取基准数。它的思路是:选取数组开头,中间和结尾的元素去比较,选择中间的值作为快排的基准。这种方式既能解决待排数组基本有序的情况,而且选取的基准也不是随机的。
eg:序列1 2 3 6 12 18,30,如果用三数取中进行快排:
因为:
数组开头是1,中间是6,结尾是30
所以:
所以基准数选择6进行快排。
快排优化方案
- 在数组长度大于某一个阈值范围时,使用递归快排,当数据长度小于阈值时,使用插入排序。因为处理较短序列时插入排序效果更好。
- 在partition中选取基准数时,选取三路取中。
- 进行三路快排优化。即将数组划分为左边小于基准数,中间等于基准数,右边大于基准数,这样两端递归时可以缩小递归数组的大小。