算法之快速排序

本文深入解析了快速排序这一高效排序算法的原理与实现过程,对比了其与归并排序的特性,详细介绍了快速排序的切分过程及代码实现,讨论了算法的性能优势与可能的低效情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思想:

快速排序是一种分治的排序算法。它将一个数组分成两个子数组,将两部分独立地排序。

快速排序和归并排序是互补的:

  • 归并排序将数组分成两个子数组分别排序,并将有序的子数组归并来使整个数组有序;而快速排序是 当两个子数组都有序时整个数组自然就有序了。
  • 归并排序的递归调用发生在处理整个数组之前,而快速排序的递归调用发生在处理整个数组之后

 

快速排序的切分:

private static int partition(Comparable[] a, int lo, int hi){
    //将数组切分为a[lo...i-1], a[i], a[i+1...hi]
    int i = lo; j = hi + 1; //左右扫描指针
    Comparable v = a[lo];   //切分元素
    while(true){
        //扫描左右,检查扫描是否结束并交换元素
        while(less(a[++i], v)) if(i == hi) break;
        while(less(v, a[--j])) if(j == lo) break;

        if(i >= j) break;
        swap(a, i, j);
    }
    swap(a, lo, i);  //将 v = a[i] 放入正确的位置
    return i;        //a[lo...i-1] <= a[i] <= a[i+1...hi] 达成
}

      切分代码按照a[lo] 的值v 进行切分。当指针 i 和 指针 j 相遇时 主循环退出。在内循环中,a[i] 小于 v 时, 增加 i,a[j] 大于 v 时,减少 j,然后交换a[i] 和 a[j] 来保证 i 左侧的元素都小于等于 v,j 右侧的元素都大于等于 v 。当指针相遇时  交换 a[lo] 和 a[i],切分结束(这样切分值就留在 a[i] 中了)。

 

快排实现:

public class Quick{
    public static void sort(Comparable[] a){
        StdRandom.shuffle(a);    //消除对输入的依赖,随机打乱
        sort(a, 0, a.length - 1);
    }

    private static void sort(Comparable[] a, int lo, int hi){
        if(lo >= hi) return;    //结束递归
        int i = partition(a, lo, hi)    //切分
        sort(a, lo, i-1);       //将左半部分a[lo...i-1]排序
        sort(a, i+1, hi);       //将右半部份a[i+1...hi]排序
    }
}

 

性能:

优点:快速排序切分方法的内循环会用一个递增的索引将数组元素和一个定值比较,这种简洁性是快速排序的一个优点,很难想象排序算法中还能有比这更短小的内循环了。比如,归并排序和希尔排序一般都比快速排序慢,其原因就是因为它们 还在内循环中移动数据。

缺点:在切分不平衡时,上述代码可能会极为低效。例如:如果第一次从最小的元素切分,第二次从第二小的元素切分,如此这般,每次调用只会移除一个元素,这会导致大数组需要切分很多次。这也就是上述代码:在快排前将数组随机排序的原因。

快速排序最多需要约 N2/2 次比较,但随机打乱数组能够预防这种情况。

 

对于大小为 N 的数组,快排的运行时间为 NlgN,归并排序也能做到这一点,但是快排一般会更快,因为移动数据的次数更少。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值