排序算法——快速排序

536 阅读2分钟

1. 算法思想

快速排序采用分治法的策略,下面应用快速排序对一个记录序列(r_{1} ~ r_{n})进行升序排序,其基本思想如下:

  • 划分:选定一个记录作为轴值,以轴值为基准将整个序列划分为两个子序列 r_{1} ~ r_{i - 1}r_{i+1} ~ r_{n},轴值的位置 i 在划分的时候确定,并且前一个子序列中的记录都小于或等于轴值,后一个子序列中的记录都大于或等于轴值。
  • 求解子问题:分别对划分后的每一个子序列递归处理。
  • 合并:由于对于子序列 r_{1} ~ r_{i - 1}r_{i+1} ~ r_{n} 的排序是就地进行的,所以合并不需要执行任何操作。

2. 具体过程

  1. 首先对待排序记录序列进行划分,以第一个记录为轴值进行划分
  2. 以轴值为基准将排序序列划分为两个子序列后,对每一个子序列分别递归进行处理

3. 动画演示

红色区域表示轴值,下面表示第一次划分的结果:

快速排序第一次划分

4. 代码实现

/**
 * @author created by linjunhao
 * @date 2020/3/26
 * @description 快速排序
 */
public class QuickSort {

    public void sort(int[] values, int start, int end) {
        int pivot;
        if (start < end) {
            //划分并获取轴值
            pivot = partition(values, start, end);
            //求解子问题,对左侧序列进行快速排序
            sort(values, start, pivot - 1);
            //求解子问题,对右侧序列进行快速排序
            sort(values, pivot + 1, end);
        }
    }

    /**
     * 划分并返回轴值
     * @param values 待划分记录
     * @param start
     * @param end
     * @return
     */
    public int partition(int[] values, int start, int end) {
        //双引用,分别从两端交替扫描
        int i = start, j = end;
        while (i < j) {
            //从尾端开始扫描
            while (i < j && values[i] <= values[j]) j--;
            //交换,将较小的记录放在前面
            if (i < j) {
                int temp = values[i];
                values[i] = values[j];
                values[j] = temp;
                i++;
            }
            //从头部开始扫描
            while (i < j && values[i] <= values[j]) i++;
            //交换,将较大的记录放到后面
            if (i < j) {
                int temp = values[i];
                values[i] = values[j];
                values[j] = temp;
                j--;
            }
        }
        //返回轴值的位置
        return i;
    }
}

5. 算法分析

  • 最好情况下,每次划分对一个轴值记录定位后,该轴值的左侧子序列和右侧子序列的长度相同,该情况下时间复杂度为 O(nlog_{2}n)
  • 最坏情况下,待排序记录序列刚好是正序或逆序,每次划分只得到一个比上一次划分少一个记录的子序列(另一个子序列为空),最坏情况下的快速排序相当于插入排序,该情况下时间复杂度为 O(n^{2})
  • 因此,平均时间复杂度为 O(nlog_{2}n)

6. 其他排序