快速排序算法详解与其他排序算法的比较

快速排序

属于原地排序,与自顶向下归并类似的是,也是先进行大数组的排序,然后递归到小数组,在进行排序时,先选定一个元素为切分元素,按大小将其他元素放到左右两侧即可

步骤:

  1. 确定切分元素,定义两个位于数组首尾的左右扫描指针
  2. 左指针遇到大于等于切分元素的元素时,停止移动;右指针遇到小于等于切分元素的元素时,停止移动;
  3. 当左右指针都停止时,交换左右指针所在元素
  4. 左右指针继续移动,交换,直到两者重合
  5. 重合后,重合位置元素与切分元素交换位置
  6. 再对切分元素左侧和右侧的元素分别进行快速排序,依次循环进行

动画演示: P为切分元素 L为左标记 R为有标记
在这里插入图片描述

package Sort;

import edu.princeton.cs.algs4.StdRandom;

import static Sort.SortExample.*;
import static edu.princeton.cs.algs4.StdRandom.uniform;
import static edu.princeton.cs.algs4.StdRandom.validateNotNull;

public class QuickSort {

    // 主函数
    public static void sort(Comparable[] a){

        StdRandom.shuffle(a);   // 调用了algs4.jar 中的源代码
        sort(a, 0, a.length-1);

        if(!isSorted(a))
            System.out.println("快速排序失败");
    }

    // 为algs4.jar 中的源代码
    // 将数组再次打乱  减少了对输入的依赖
    public static void shuffle(Object[] a) {
        validateNotNull(a);   // 判断数组是否为空
        int n = a.length;
        for (int i = 0; i < n; i++) {
            int r = i + uniform(n-i);     // between i and n-1
            // 交换位置操作
            Object temp = a[i];
            a[i] = a[r];
            a[r] = temp;
        }
    }
    // 为 algs4.jar 中的源代码
    private static void validateNotNull(Object x) {
        if (x == null) {
            throw new IllegalArgumentException("argument is null");
        }
    }

    private static void sort(Comparable[] a, int lo, int hi){
        if(hi <= lo)
            return;
        int j = partition(a, lo, hi);
        sort(a, lo, j-1);
        sort(a, j+1, hi);
    }

    // 将数组分为 a[lo...,i-1] a[i] a[i+1..hi]
    private static int partition(Comparable[] a, int lo, int 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;
            exch(a, i, j);   // 当左右标记满足条件停止移动时,交换标记所在的元素
        }
        exch(a, lo, j);    // 将 v= a[j] 放入正确的位置
        return j;          // 返回切分元素索引,此时a[j] 左右两侧完成排序
    }
}

特点:

  • 快速排序如同名字一样,在大多数情况下,具有良好的性能优势
  • 大多数编程语言的数组排序内置函数都采用快速排序实现

注意点:

  • 当有元素值与切分元素重复时,需要防止循环意外中断
  • 保持随机性,不依赖输入,尽量将切分元素也进行随机化

时间复杂度:
N l g N NlgN NlgN

四种排序算法的比较

数据来源:algo4 官方提供的 algo4-data.zip 数据包

进行1k数据量的比较:

选择排序,1000个数据,执行时间为:0.0115291 s
插入排序,1000个数据,执行时间为:0.0105386 s
希尔排序,1000个数据,执行时间为:0.0016667 s
自顶向下归并排序,1000个数据,执行时间为:0.0017786 s
自底向上归并排序,1000个数据,执行时间为:6.685E-4 s
快速排序,1000个数据,执行时间为:0.0027377 s

进行32k数据量的比较:

选择排序,32000个数据,执行时间为:4.200542 s
插入排序,32000个数据,执行时间为:2.5466117 s
希尔排序,32000个数据,执行时间为:0.0212745 s
自顶向下归并排序,32000个数据,执行时间为:0.0246908 s
自底向上归并排序,32000个数据,执行时间为:0.0140204 s
快速排序,32000个数据,执行时间为:0.021905 s

其他有关排序算法的文章:
选择排序,插入排序,希尔排序的详解与比较

归并排序详解,与其他排序算法的比较

posted @ 2022-02-10 12:00  清澈的澈  阅读(31)  评论(0编辑  收藏  举报  来源