排序算法之——快速排序多方案实现

快速排序概述


快速排序是我们最常用的一种排序方法之一,它使用了分治思想。快速排序是对冒泡排序的一种改进。

原理

  1. 通过一趟排序,将原数据分割为独立的2个部分,其中一部分的所有数据均比另一部分的所有数据小。
  2. 然后,再按照此方法对这两部分的数据进行快速排序,整个排序过程可以递归进行。
  3. 最终,整个数据编程有序序列。

复杂度

时间复杂度:最好情况是O(nlogn),最差情况是O(n²),它的平均时间复杂度为O(nlogn)。

快速排序的平均性能非常好,通常是实际排序应用中最好的选择。

空间复杂度:快速排序是一种原址排序,只需要一个很小的栈作为辅助空间,它的空间复杂度为O(logn),所以适合在数据集比较大的时候使用。

步骤

快速排序的三步分治过程:

  • 分解:数组A[s…e]被划分为2个子数组A[s…q-1]和A[q+1…e],使得A[s…q-1]中的每个元素都小于等于A[q],而A[q]也小于等于A[q+1…e]中的每个元素。其中计算下标q也是划分过程的一部分。
  • 解决:通过递归调用对子数组进行排序。
  • 合并:因为子数组都是原址排序,所以不需要合并操作。

快速排序的实现


这里介绍2个方案,我们来看。

方案一

public static void quickSort(int[] arr, int s, int e){
    if(s<e){
        int q = partition(arr, s, e);
        quickSort(arr, s, q-1);
        quickSort(arr, q+1, e);
    }
}
private static int partition(int[] arr, int s, int e){
    int base = arr[e];//选出最后一个作为主元(对比基数)
    int i = s - 1;//小于主元值的数组边界。开始时为-1。
    int t;
    for(int j=s;j<e-1;j++){//找出小于主元的值
        if(arr[j] <= base){
            i++;//小于主元的数组边界i增加一个
            if(i != j){//防止它们相等时执行交换,原数组序列正确时会出现
                //这时,j位置的元素小于base值,i位置的元素是大于主元值的(想一想这是为什么?),这里执行交换
                t = arr[i];
                arr[i] = arr[j];
                arr[j] = t;
            }
        }
    }
    //最后需要把小于等于主元的值边界再次扩张1,这时A[i+1]是大于主元的,所以把主元A[r]和它进行交换,本次排序结束。
    t = aar[i+1];
    aar[i+1] = aar[e];
    aar[e] = t;
    return i+1;//返回主元的当前位置
}

方案一的具体实现逻辑请看注释,这里简单归纳一下:

  1. quickSort方法负责快速排序过程,参数为数组arr,数组起始元素位置s,数组结尾元素位置e。
  2. 当s<e时。
  3. 以数组尾部元素为主元,找出主元在一轮排序完成后的正确位置,这里执行方法partition来完成。
  4. 最后递归调用,对主元素左侧和右侧执行快速排序过程。
  5. partition中,以数组尾部元素为主元,i是小于主元的数组边界,通过一个for循环遍历,查找小于主元的值,依次添加到当前i+1的位置,并且扩展小于主元的数组边界i。循环结束后,将主元位置放在正确的地方,然后返回当前位置即可。

方案二

public static void quickSort(int[] arr, int s, int e){
    int i,j;//i为小于主元边界,j为大于主元的边界
    int base = arr[s];//主元(对比的基值)
    int t;//临时变量
    if(s>e){
        return;
    }
    i = s;
    j = e;
    while(i<j){
        //先看右边,依次往左递减
        while(base <= arr[j]&&i<j){
            j--;
        }
        //再看左边,依次往右递增
        while(base >= arr[i]&&i<j){
            i++;
        }
        //如果满足条件则交换
        if(i<j){
            t = arr[j];
            arr[j] = arr[i];
            arr[i] = t;
        }
    }
    //最后将基准为与i和j相等位置的数字交换
    arr[s] = arr[i];
    arr[i] = base;
    quickSort(arr, s, j-1);
    //递归调用右半数组
    quickSort(arr, j+1, e);
}

方案二中,思想和方案一是类似的,但是我们使用了2个标记为指针来指示小于主元边界和大于主元边界,最后执行递归完成排序过程。

总结


  1. 快速排序是我们最常用的一种排序方法之一,它使用了分治思想。快速排序是对冒泡排序的一种改进。
  2. 通过一趟排序,将原数据分割为独立的2个部分,其中一部分的所有数据均比另一部分的所有数据小。然后,再按照此方法对这两部分的数据进行快速排序,整个排序过程可以递归进行。最终,整个数据编程有序序列。
  3. 快速排序的时间复杂度:最好情况是O(nlogn),最差情况是O(n²),它的平均时间复杂度为O(nlogn)。
  4. 快速排序是一种原址排序,只需要一个很小的栈作为辅助空间,它的空间复杂度为O(logn),所以适合在数据集比较大的时候使用。
  5. 快速排序的平均性能非常好,通常是实际排序应用中最好的选择。
  6. 最后我们在实战中,使用了2种方式,实现了快速排序的过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卜大爷

觉得不错的可以给我加油哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值