快速排序算法(Java)

109 阅读3分钟

快速排序(Java)

先大致介绍一下快排,快排是用一种分治的思想,递归的方法来实现。分治就是说每次把区间[l,r]根据其中某个数的值,按照大小关系分成左右两部分,比如左边小,右边大,然后再把左右区间如何不断如此处理,直到最后的区间里剩下一两个数字,也就是整个区间无论是划分为大区间还是小区间最后都保持了左边小右边大的性质,那么可以理解为排好序了。递归呢,是我们实现分治的一种方式,是函数自己调用自己,保证他能不断深入去处理区间,像是套娃一样。

举例说明

[5, 4, 6, 1, 2, 3] ,以此为例子进行排序,基本的思路就是先选择一个基准值进行左右区间划分,然后设置两个坐标i和j,一个从前往后找不小于基准值的,一个从后往前找不大于基准值的,找到之后进行交换,直到i和j碰撞。

📦 初始数组:

[5, 4, 6, 1, 2, 3] arr
[0, 1, 2, 3, 4, 5] idx

第一轮:开始用中间值mid=6作为基准,i从0往后移动,j从5往前移动,i到2停止,j在5停止,交换后的结果为

[5, 4, 3, 1, 2, 6] arr
[0, 1, 2, 3, 4, 5] idx
       i        j

然后i和j还没碰撞,就继续寻找,变成

[5, 4, 3, 1, 2, 6] arr
[0, 1, 2, 3, 4, 5] idx
                ij

i和j碰撞之后呢,就开始递归下一轮了,数组也划分为两部分

[5, 4, 3, 1, 2]                [ 6] arr
[0, 1, 2, 3, 4]                [ 5] idx
 i           j                 只有一个数字,就到此为止了               

开始第二轮呢,在左右区间分别进行上述重复操作,直到左右两个区间里面都是一个数字。

Java代码

// 这里的l是数组的左边界,r是数组的右边界
public static void quickSort(int[] arr, int l, int r){
    if (l >= r) return;
    int mid = arr[l + (r - l) / 2];
    int i = l - 1; // 这里使用i和j都在边界的外面,是为了和下面的++i和--j配合
    int j = r + 1;
    while (i < j){
        while (arr[++i] < mid);
        while (arr[--j] > mid);
        if (i < j){
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
    }
    quickSort(arr,l,j);
    quickSort(arr,j+1,r);
}

算法分析

时间复杂度

这里的时间复杂度主要是分为两个板块,一个是区间划分,第二个每一轮的扫描交换。区间划分多少次呢?理想情况下每次都左右划分的区间长度差不多,那么就是log2(n),极度不理想的情况呢,就是一个区间只有一个数字,另一个有n-1个数字,这样子会划分(n-1)次才可以。在每一轮的扫描中呢,无论如何也是i和j的碰撞,那么自然是全部数据都扫描了一遍,可以理解为n。汇总起来呢就是从nlog2(n)~n^2。

稳定性

不稳定,这里的稳定性指的是相同的数据,比如5 2 2 1 4中的第一个2和第二个2,在排序结束后时候顺序是否发生变化。 这是因为我们每一轮都需要i和j碰撞,那么i和j停留在第一个和第二个2上,需要进行交换,然后往前继续推进,变成了j在i前面,实现碰撞,自然也就不符合稳定性的。那么如何想要保持稳定性呢,可以带着坐标索引一起排序,最后根据坐标重整。