收藏

快速排序(Quick Sort)

2021-11-20  本文已影响0人  小波同学

一、算法概述

1.1 算法分类

十种常见排序算法可以分为两大类:

1.2 算法复杂度

1.3 相关概念

二、快速排序(Quick Sort)

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

2.1 算法描述

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

2.2 动图演示

QuickSort

2.3 排序过程

下面以数列a={30,40,60,10,20,50}为例,演示它的快速排序过程(如下图)。


上图只是给出了第1趟快速排序的流程。在第1趟中,设置x=a[i],即x=30。

三、快速排序分类

快速排序分为:

这里介绍单边循环快排(lomuto 洛穆托分区方案)双边循环快排(并不完全等价于hoare霍尔分区方案)

3.1 单边循环快排(lomuto 洛穆托分区方案)

/**
 * @author: huangyibo
 * @Date: 2021/11/17 18:38
 * @Description: 快速排序——单边循环快排(lomuto 洛穆托分区方案)
 * 1、选择最右元素作为基准点元素
 * 2、j指针负责找到比基准点小的元素, 一旦找到则与i进行交换
 * 3、i指针维护小于基准点元素的边界, 也是每次交换的目标索引
 * 4、最后基准点与i元素交换, i即为分区位置
 */
public class QuickSort1 {

    public static void main(String[] args) {
        int[] arr = {5, 9, 7, 4, 1, 3, 2, 8};

        quickSort(arr,0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }

    public static void quickSort(int[] arr, int left, int right) {
        //当指定区间范围内元素值小于等于1的时候, 停止递归调用
        if(left >= right){
            return;
        }

        //pv 基准点的正确索引值
        int pv = partition(arr, left, right);

        //左边分区的范围确定
        quickSort(arr, left,pv - 1);

        //右边分区的范围确定
        quickSort(arr, pv + 1, right);
    }

    public static int partition(int[] arr, int left, int right) {
        //基准点元素
        int pivot = arr[right];
        int i = left;
        for (int j = i; j < right; j++) {
            if(arr[j] < pivot){
                if(i != j){
                    //i和j不相等的时候才交换元素
                    swap(arr, i, j);
                }
                i++;
            }
        }
        if(i != right){
            //一轮循环之后,基准点元素和小于基准点元素边界值交换
            swap(arr, right, i);
        }

        //返回值代表了基准点元素所在的正确索引,它确定下一轮分区的边界
        return i;
    }

    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

3.2 双边循环快排(并不完全等价于hoare霍尔分区方案)

/**
 * @author: huangyibo
 * @Date: 2021/11/17 18:41
 * @Description: 快速排序——双边循环快排(并不完全等价于hoare霍尔分区方案)
 * 1、选择最左元素作为基准点元素
 * 2、j指针负责从右向左找比基准点小的元素, i指针负责从左向右找比基准点大的元素, 一旦找到二者交换, 直到i、j相交
 * 3、最后基准点与i(此时i与j相等)交换, i即为分区位置
 *
 * 特点:
 * 1、平均时间复杂度是O(n ㏒₂n), 最坏时间内复杂度为O(n²)
 * 2、数据量较大时,优势非常明显
 * 3、属于不稳定排序算法
 */
public class QuickSort2 {

    public static void main(String[] args) {
        int[] arr = {5, 9, 7, 4, 6, 1, 3, 2, 8};

        quickSort(arr,0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }

    public static void quickSort(int[] arr, int left, int right) {
        //当指定区间范围内元素值小于等于1的时候, 停止递归调用
        if(left >= right){
            return;
        }

        //pv 基准点的正确索引值
        int pv = partition(arr, left, right);

        //左边分区的范围确定
        quickSort(arr, left,pv - 1);

        //右边分区的范围确定
        quickSort(arr, pv + 1, right);
    }

    public static int partition(int[] arr, int left, int right) {
        //基准点元素
        int pivot = arr[left];
        int i = left;
        int j = right;
        while (i < j){
            //j指针负责从右向左找比基准点小的元素, 必须先从右向左找,先执行j指针
            while(i < j && arr[j] > pivot){
                j--;
            }
            //i指针负责从左向右找比基准点大的元素
            while(i < j && arr[i] <= pivot){
                i++;
            }

            if(i != j){
                //i和j不相等的时候才交换元素
                swap(arr, i, j);
            }
        }
        //一轮循环之后, i指针和j指针相交, 基准点元素和j指针(i、j指针相等)元素交换
        if(j != left){
            swap(arr, left, j);
        }

        //返回值代表了基准点元素所在的正确索引,它确定下一轮分区的边界
        return j;
    }

    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

3.3 快速排序特点

参考:
https://www.cnblogs.com/onepixel/articles/7674659.html

https://www.cnblogs.com/skywang12345/p/3596746.html

上一篇下一篇

猜你喜欢

热点阅读