快速排序
什么是快速排序
和归并排序一样,快速排序也使用分而治之的算法。
快速排序是从冒泡排序演变过来的算法,但是快速排序性能远远优于冒泡排序。
快速排序的基本步骤:
- 快速排序会从数列中选择一个值作为基准元素(基准元素的挑选和作用,在”相关概念”中有介绍)
- 并创建双指针引用(左指针和右指针,在”相关概念”中有介绍)
- 移动左指针直到找到一个比基准元素大的值,接着,移动右指针直到找到一个比基准元素小的值,然后交换他们,不断重复这个过程,直到左指针超过了右指针。此时所有比基准元素小的值都排在了基准元素的左侧,比基准元素大的值都排在了基准元素的右侧。
- 接着进行划分操作——partition,算法对划分后的小数组(较主元小的值组成的子数组,以及较主元大的值组成的子数组)进行前面相同的步骤,直至数组已完全排序。
相关概念
基准元素
基准元素,英文pivot,快速排序会在每一轮挑选一个基准元素,作为分而治之法的中心,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成了两个部分。
基准元素如何选择呢?
基准元素的选择方式有很多,常见的有选择数列的第一个元素,数列中间的元素,甚至可以随机选择一个元素作为基准元素。
本文例子以数列中间的元素作为基准元素。
基准元素的选择没有绝对的好坏,取决于选择的基准元素,选到数列的最大值或最小值的概率,导致时间复杂度的变化。
快速排序的平均时间复杂度是 O(nlogn),最坏情况下的时间复杂度是 O(n^2)
左指针
快速排序使用双指针法,用于元素交换前的元素扫描,分为左指针和右指针。
左指针初始指向数列第一个元素,左指针目标是找到一个比基准元素要大的元素。
右指针
快速排序使用双指针法,用于元素交换前的元素扫描,分为左指针和右指针。
右指针初始指向数列最后一个元素,右指针目标是找到一个比基准元素要小的元素。
图解快速排序
假如对这样一组数列进行排序
选择最右边的数字作为基准元素(选择基准元素的方法有很多种,这只是其中一种)
并创建双指针引用,左指针初始指向数列第一个元素,右指针初始指向数列最后一个元素。
移动左指针直到找到一个比基准元素大的值,此时找到8
移动右指针直到找到一个比基准元素小的值,此时找到4
然后交换他们,也就是交换8和4
重复相同的步骤,继续从左标记开始移动,直到找到一个比基准元素大的值,此时找到9
从右标记开始移动,但是此时没有左右标记碰到了一起,也会停止移动,并交换这个数字与基准元素的位置。
此时所有比基准元素小的值都排在了基准元素的左侧,比基准元素大的值都排在了基准元素的右侧。
接着进行划分操作——partition,把基准元素之前的元素划分为左侧子数组[3,5,4,1,2],基准元素之后的元素划分为右侧子数组[8,7,9],先对左侧子数组进行上述相同的操作。
此时操作后的左侧子数组也能拆出更小的子序列,在这里,左侧子数组为2之前的元素,右侧子数组为2之后,6之前的元素,也就是[4,3,5]。
因为左侧只有一个数字1,则认为已完成排序,不需要操作。此时操作右侧子数组[4,3,5]。
也重复上述相同的操作,直至所有数字排序完成。
操作完最外层的左侧子数组,则继续对右侧子数组进行上述相同的操作。
最终所有数字完成排序。
快速排序动画图解
代码实现
1 |
|