写在前边数据库
咱们有这么一个需求,老板和咱们说,要求咱们作这么一个员工系统,公司员工的相关信息和为公司的贡献值都会在这个系统进行记录,每到月底评功轮赏的时候,根据员工这一个月的表现进行奖罚。你可能会说,这还很差作吗?增删改查,而后直接按照贡献值从大到小排序就行了。数组
别着急,还有一个需求就是公司每月都会进行抽奖福利,抽奖的方式是,老板随机抽取贡献值为第 K 大的贡献值的员工送出福利一份,共选取 n 位,而不是评功论赏了,若是让你实现一个系统,你该如何实现呢?框架
若是你学完今天的快速排序,就很轻松的解决老板给你分配的任务啦。ide
思惟导图函数
1性能
什么是快速排序?大数据
顾名思义,快速排序,那确定快呀,那到底有多快呢?快不过三秒?动画
假如咱们已经接过老板在数据库给咱们取出的本月每一个员工的信息,咱们单独筛选出贡献值,以下数据。3d
为了可以更加清晰的讲解,咱们对一些用到的特殊数据进行标识一下。blog
如上数据,咱们从 p 到 q 随机找一个元素做为区分点(pivot),什么是区分点?稍后咱们解释。咱们就选择最后一个数据 5 吧,而后咱们以 5 为区分点,而后从 p 开始遍历元素,若是当前遍历的元素小于 5,咱们就放在 5 的前边,若是当前遍历的元素大于 5,咱们就放在 5 的后边,最后的结果以下:
看了上边的一顿操做,咱们也明白了为何 5 是区分点了。上边的数据也没有从小到大呀?别着急,重点来了。
咱们是总体数据按照 5 为区分点进行从新排列数据的,若是咱们使用一样的方式分别对 5 左边和 5 右边的数据分别进行这种方式的划分,直到划分到区间为 1 为止,是否是数据就会变的有序了?没错,这就是咱们所说的快速排序。
有小伙伴会问到,这多麻烦,也快不过三秒呀?咱们后边会有性能分析的,到时候就知道快排比咱们以前讲的冒泡、插入有多快了。
2
动画实现
3
快速排序的原理
虽然咱们上边笼统的分析了快速排序的基本过程,可是其中有两个中要的知识点,快速排序的过程用到了递归和分治思想,咱们分开进行分开讲解。
一、
递归
首先看一下快速排序的递推公式,咱们不断的将大区间分割成小区间,而后对小区间再次进行分割。
咱们能够总结出以上的递推公式。
由于咱们不断的将大区间分红小区间,而后一直分下去,不对,一直分总有一个尽头的,因此这也是递归的终止条件。当知足这个条件时,就再也不继续往下进行递归,那么快速排序的递归条件是什么呢?上边也说到了,当区间只剩一个数据的时候,咱们再也不进行划分,因此递归条件为:
p >= q
递归的代码实现:
二、
分治思想
咱们之因此将大问题不断的分红小问题,就是用到的分治思想,分而治之,将分解的小问题解决了,大问题天然而然就会获得解决。
最关键的是快速排序中有一个分区函数 partition,分区函数的做用就是随机找到一个区分点 pivot,而后对数据进行分区,该函数会返回分区后 pivot 的下标。
咱们好奇的是如何进行分区的?咱们须要用到一个分区函数 partition,咱们想到最简单的方法可能就是小于 pivot 的元素放到数组 a 中,大于 pivot 的元素放到数组 b 中,而后合并 a 和 b,完成分区。
若是咱们不考虑空间上的消耗的话,这样写没毛病的。可是,为了考虑到空间上的消耗,也就是咱们但愿空间复杂度是 O(1),不得不让分区函数占用少的内存空间,咱们须要在原数组中完成分区,而不是另外开辟新的空间。
这个过程咱们单纯的想是很难想出来的,并且很是有技巧性,因此咱们一块儿来看一下。咱们仍是以上边的数据为例,从 p 开始遍历元素,分别和 pivot 区分点元素进行比较,若是小于区分点元素,咱们就进行交换,若是大于区分点元素,咱们就不进行交换,咱们具体来看一下动画的实现。
4
快速排序的性能
咱们知道快速排序的整个实现过程了,下面咱们来分析一下快速排序的性能如何,不是你说很快嘛?能快过三秒吗?
时间复杂度
咱们先来看时间复杂度,快速排序时间复杂度的计算是分区操做的时间加上合并的时间,快速的时间复杂度为 O(nlogn)。这是理想状况下,为何呢?由于咱们随机选择区分点不可能每次都能将数据一分为二。
还有一种极端的状况就是,若是原数据是一组有序数据,若是每次都要选择最后一个元素为区分点,大约须要进行 n 次操做,每次遍历 n/2 个元素,因此时间复杂度就会推化成 O(n²)。
虽然存在这种状况,可是这种状况的几率是极低的,并且咱们有方法能够将这种方法降到最低,在基础环节,咱们很少啰嗦。快速排序大部分状况下的平均时间复杂度为 O(nlogn)。
空间复杂度
咱们上边也特别强调了,咱们分区函数只需在原数组中进行分区操做就能够完成,不须要开辟额外的内存空间,因此空间复杂度为 O(1)。
快速排序不管是时间效率仍是空间效率,足以比咱们以前讲的冒泡排序和插入排序要效率高的多,在一些排序函数的框架源码中,咱们也会使用到快速排序,因此快排的应用仍是很是普遍的,所谓快不过三秒“真男人”。
5
代码实现
JavaScript 版本
Java 版本
6
小结
咱们回到文章开头的问题上,咱们有一组员工的贡献值数据,咱们要随机选取第 K 大的贡献值的员工发放奖品,如何实现呢?
你可能会问,今天讲的快速排序和这个问题有什么直接的挂钩呢?表面看起来并无什么挂钩,而这个问题的解决是对快速排序代码的一个变体,稍微改动一下,就能够轻松解决上述问题。
好比几位员工的贡献值以下:七、九、四、三、六、二、5 。第 4 大元素就是 5,那就恭喜贡献值为 5 的员工得到奖金一份,虽然实际状况下不太可能用这种方式发奖品,这里咱们只是拿这个例子来说。
咱们将上边的数据像快速排序同样分为三部分,分别为 [0,p-1] p [p,q],这是已经完成分区函数的数据,由于咱们从 0 开始的,而后判断当前的 p + 1 是否等于 K?若是等于 K ,那么数组中下标为 p 的元素就是第 K 大数据。
图片
如上图的 5 就是第四大数据,可是它在数组中的下标为 3,因此须要加 1。