一文教你理解排序算法——快速排序
介绍
排序想必大家都不会陌生,在日常的业务场景当中,排序是一个很常见的功能,按照分数排序、按照排名排序等等,数组对于排序也很贴心的给出了相应的高阶函数sort()。相信大家在日常开发当中用的肯定不少,不过俗话说得好,授人以鱼不如授人以渔。我们不仅要会使用 api,在面对特殊的业务场景的时候,我们也可以自己上阵。本文我们就来探究一下快速排序是实现。
快速排序

快速排序算法由于它的方便和高效在实践中得到广泛的应用。快速排序在平均情况下的时间复杂度为 O(nlogn),在这个意义下,快速排序算法是平均情况下效率最高的算法之一。
快速排序伪码实现
算法 Quicksort 输入:数组 A[p,...,r],1<=p<=r<=n //p,r 分别为数组 A 的首元素和尾元素的下标 输出:从 A[p]到 A[r]按照递增顺序排好序的数组 A
- if p < r
- then q <- Partition(A,p,r)
- A[p] <-> A[q]
- Quicksort(A,p,q-1)
- Quicksort(A,q+1,r)
算法 Partition 输入:数组 A[p,r] 输出:j,A 的首元素在排好序的数组中的位置
- x <- A[p]
- i <- p
- j <- r
- while true do
-
repeat j <- j - 1; -
until A[j] <= x -
repeat i <- i + 1 -
until A[i] > x -
if i < j -
then A[i] <-> A[j] -
else return j
原理解析
快速排序的思想是先利用首元素为标准,从前往后寻找第一个比首元素大的元素,从后往前找第一个比首元素小的元素。在算法当中,这种思想称为分治策略。分治策略使用的场景很多,比如二分查找,归并排序,汉诺塔等等。这些以后我们有时间再来实现。上面的伪码当中,先判断首元素下标是否比尾元素下标大,如果大于或等于的话,说明数组已经全部排序完成。然后通过Partition来获取首元素在排序好的数组当中的位置,然后和首元素和那个位置上的元素进行交换,这样首元素就被插入到合适的地方。现在我们已经排序好了一个数,以这个数为分割点,数组一分为二,前面的都比分割点小,后面的都比分割点大,因此,我们使用递归来不断地对这两部分进行排序。而Partition算法实现的是获取首元素在排好序当中的位置下标。首先获取首元素,作为标准。然后把首元素下标赋值给 i,把尾元素下标赋值给 j。通过双层 while 来不断比较。先从后往前扫描数组 A,找到第一个比首元素小的数,这是 A[j]后面的数都比首元素大。然后从前往后扫描数组 A,找到第一个比首元素大的数 A[i],然后进行交换,这样后面比首元素小的数就到了前面,前面比首元素大的数就到了后面。接着继续寻找,直到 i>j 时,整个数组都被扫描完成。然后返回 j,这时候 j 就是首元素我们应该在的下标。
Java 实现
package com.sort;
public class Quicksort {
//排序的数组
int array[];
//首元素下标
int low;
//尾元素下标
int height;
//初始化类
public Quicksort(int array[]){
this.array = array;
this.low = 0;
this.height = array.length - 1;
quicksort(this.array,this.low,this.height);
}
//进行排序
public void quicksort(int array[],int low,int height){
int q,temp;
if(low < height){
//获取位置
q = partition(array,low,height);
//将首元素插入到合适的位置
temp = array[low];
array[low] = array[q];
array[q] = temp;
//使用递归,将以标准元素分出来的区域进行排序
quicksort(array,low,q-1);
quicksort(array,q+1,height);
}
}
//排序
public int partition(int array[],int low,int height){
//先将首元素设为对比标准
int normalEle = array[low];
//将首元素给i,同时跳过第一个元素不比较
int i = low + 1;
//将尾元素给j
int j = height;
//定义临时交换变量
int temp;
//开始寻找标准元素所处的位置
while (true){
//从头开始遍历,寻找第一个比标准大的数,同时首元素下标不能比尾元素大
while (array[i] <= normalEle){
i++;
}
//从尾开始遍历,寻找第一个比标准小的数,同时首元素下标不能比尾元素大
while (array[j] > normalEle){
j--;
}
if(i < j){
//将首元素和尾元素所找到的元素进行交换
temp = array[i];
array[i] = array[j];
array[j] = temp;
}else {
//遍历完一轮,返回本轮找到的标准元素所在的下标
return j;
}
}
}
}
TS 实现
function quick(array: number[]) {
//不是数组字节返回
if (!Array.isArray(array)) {
return;
}
//首元素下标
let low: number = 0;
//尾元素下标
let height: number = array.length - 1;
quickSort(array, low, height);
}
function quickSort(array: number[], low: number, height: number) {
let swap: number;
let temp: number;
if (low < height) {
//获取标准元素应该在数组当中的下标
swap = partition(array, low, height);
temp = array[low];
array[low] = array[swap];
array[swap] = temp;
quickSort(array, low, swap - 1);
quickSort(array, swap + 1, height);
}
}
function partition(array: number[], low: number, height: number): number {
//首元素下标,跳过第一个首元素
let i: number = low + 1;
//尾元素下标
let j: number = height;
//比对标准
let normalEle: number = array[low];
//交换变量
let temp: number;
while (true) {
//从前往后寻找第一个比标准元素大的元素
while (array[i] < normalEle) {
i++;
}
//从后往前找,寻找第一个比标准元素小的元素
while (array[j] > normalEle) {
j++;
}
//如果i<j,那么两个元素交换
if (i < j) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
} else {
return j;
}
}
}
export default quick;