快速排序(Quick Sort)

1 摘要

时间复杂度: 最好、平均 O ( n l o g n ) O(nlogn) O(nlogn);最坏 O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( l o g n ) O(logn) O(logn)
是否稳定: 不稳定

2 算法细节

2.1 算法思想

如果要对数组区间 [p, r] 的数据进行排序,

  • 选择其中任意一个数据作为 pivot(分支点),一般为区间最后一个元素
  • 然后遍历数组,将小于 pivot 的数据放到左边,将大于 pivot 的数据放到右边。
  • 接着,我们再递归对左右两边的数据进行排序
  • 直到区间缩小为 1 ,说明所有的数据都排好了序。
    快速排序图解
    递推公式:
    quick_sort(p…r) = quick_sort(p…q-1) + quick_sort(q+1, r)
    
    终止条件:
    p >= r
    

快速排序的具体分区过程如下所示:

  • 从左到右依次遍历数组,定义 i 和 j 指针,其中 i 指向大于pivot的元素的起始位置,j 用于遍历所有元素,以便将其与pivot进行比较
    • 初始化时,均指向数组起始位置;
  • 如遇到小于 pivot 的元素,则进行数据交换,i 与 j 所指的数据进行交换,即将小于pivot的元素放于索引 i ,紧接着 i 会加一;否则继续往前进行(即不做任何交换,两个指针各加1);
  • 最后再将 pivot 放于大于 pivot 和小于 pivot 的分界位置,即 p 与 i 所指数据进行交换。
    在这里插入图片描述

2.2 算法实现

def quickSort(nums, start, end):
    # 由于本算法是直接对数组进行改变,所以针对于非法输入的数组可以直接忽略
	if start < end:
		pivot = nums[end]
		i = j = start
		while j < end:
			if nums[j] < pivot:
				nums[i], nums[j] = nums[j], nums[i]
				i += 1
			j += 1
		# 此时j = end,所以下式等价于交换nums[j]和nums[i]
		nums[i], nums[end] = nums[end], nums[i]
		quickSort(nums[:i], start, i-1)
		quickSort(nums[i+1:end], i+1, end)
	# 直接对数组进行排序操作,改变索引,没有返回值
  • 递归调用时,注意终止条件,当nums的长度小于2时,直接返回;
  • 当长度大于等于2时,进行排序,并修改nums中元素的位置。

2.3 与归并排序的对比

  • 归并排序是由下向上的,先处理子数组然后再合并。
    • 归并排序非常稳定,时间复杂度始终都是 O ( n l o g n ) O(nlogn) O(nlogn),但它是非原地算法
  • 而快速排序正好相反,它的过程是由上向下的,先分出两个子区间,再对子区间进行排序。
    • 最坏情况:如果数据原来已经是有序的,则每次的分区都是不均等的,我们需要进行 n 次分区才能完成整个排序,此时快排的时间复杂度就退化成了 O ( n 2 ) O(n^2) O(n2),但平均情况下时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),最坏情况发生的概率也比较小;
    • 原地排序算法,因此应用得更加广泛。
    • 是一个不稳定的排序算法,因为其在数据交换过程中可能会改变相等元素的原始位置。
      归并与快速排序的对比

参考:

  1. https://zhuanlan.zhihu.com/p/47067858
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值