(六) 数据结构 - 快速排序

快速排序

快速排序是一种排序执行效率很高的排序算法,它利用分治法来对待排序序列进行分治排序,它的思想主要是通过一趟排序将待排记录分隔成独立的两部分,其中的一部分比关键字小,后面一部分比关键字大,然后再对这前后的两部分分别采用这种方式进行排序,通过递归的运算最终达到整个序列有序。

核心思想

快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

代码开发

实现思路

Step 1-从数组中挑选一个元素作为基准值(从左边和右边开始都可以)
Step 2-将两个变量分别指向数组的左侧和右侧(不包括基准值)
Step 3-左指向低索引
Step 4-右指向高索引
Step 5-比基准值小的放置在基准值前面
Step 6-比基准值大的放置在基准值后面(相同数据可以放置任意一边)
Step 7-递归(其中包括确定新的基准值)把小于基准值元素的子数列和大于基准值元素的子数列排序

伪代码

package com.paal.demo.c01SortingBasic;

import com.paal.demo.Sort;
import com.paal.demo.utils.SortTestHelper;

/**
 * <p/>
 * <li>title: 快速排序-基础版</li>
 * <li>@author: li.pan</li>
 * <li>Date: 2019/12/10 22:50 下午</li>
 * <li>Version: V1.0</li>
 * <li>Description: todo</li>
 */
public class QuickSort implements Sort {

    /**
     * 快速排序入口
     *
     * @param arr 数组
     */
    @Override
    public void sort(Integer[] arr) {

        int n = arr.length;
        sort(arr, 0, n - 1);
    }

    /**
     * 递归使用快速排序,对arr[l...r]的范围进行排序
     *
     * @param arr 无序集合
     * @param l   低索引
     * @param r   高索引
     */
    public static void sort(Integer[] arr, int l, int r) {

        // 递归到底
        if (l >= r)
            return;
        int p = partition(arr, l, r);
        sort(arr, l, p - 1);
        sort(arr, p + 1, r);
    }

    /**
     * 比基准值小的放置在基准值前面,比基准值大的放置在基准值后面(相同数据可以放置任意一边)
     * 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
     *
     * @param arr 无序数组
     * @param l   低索引
     * @param r   高索引
     * @return
     */
    private static int partition(Integer[] arr, int l, int r) {

        Integer v = arr[l];

        int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
        for (int i = l + 1; i <= r; i++)
            if (arr[i] < v) {
                j++;
                SortTestHelper.swap(arr, j, i);
            }

        SortTestHelper.swap(arr, l, j);
        return j;
    }
}
/**
     * 快速排序测试方法
     */
    @Test
    public void quickSortTest() {

        Integer[] integers0 = SortTestHelper.generateRandomArray(100, 0, 1000000);
        Integer[] integers1 = SortTestHelper.generateRandomArray(10000, 0, 1000000);
        Integer[] integers2 = SortTestHelper.generateRandomArray(100000, 0, 1000000);
        System.out.println("------------------------------随机数组--------------------------------");
        System.out.println("插入排序测试1数据量为100" + SortTestHelper.testSort(integers0, new InsertionSort()));
        System.out.println("插入排序测试2数据量为10000" + SortTestHelper.testSort(integers1, new InsertionSort()));
        System.out.println("插入排序测试3数据量为100000" + SortTestHelper.testSort(integers2, new InsertionSort()));
        System.out.println("归并排序优化测试1数据量为100" + SortTestHelper.testSort(integers0, new MergeSortOptimize()));
        System.out.println("归并排序优化测试2数据量为10000" + SortTestHelper.testSort(integers1, new MergeSortOptimize()));
        System.out.println("归并排序优化测试3数据量为100000" + SortTestHelper.testSort(integers2, new MergeSortOptimize()));
        System.out.println("快速排序测试1数据量为100" + SortTestHelper.testSort(integers0, new QuickSort()));
        System.out.println("快速排序测试2数据量为10000" + SortTestHelper.testSort(integers1, new QuickSort()));
        System.out.println("快速排序测试3数据量为100000" + SortTestHelper.testSort(integers2, new QuickSort()));

    }

执行结果

------------------------------随机数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.005s
插入排序测试2数据量为10000排序时长为: 0.089s
插入排序测试3数据量为100000排序时长为: 5.728s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.001s
归并排序优化测试3数据量为100000排序时长为: 0.005s
快速排序测试1数据量为100排序时长为: 0.0s
快速排序测试2数据量为10000排序时长为: 0.046s
快速排序测试3数据量为100000排序时长为: 6.721s

代码优化

  • 优化一:对于数据量较小的数组, 使用插入排序的性能反而更快, 所以可以在partition到底层数据量较小的时候, 使用插入排序替换快速排序

  • 优化二:因为数组可能是近乎有序的数组, 所以我们可以不每次取队首的元素, 可以采取每次随机取一个元素

  • 优化三(三路快排):对于存在大量重复元素的数组, 我们可以不仅仅将数组分割成两部分, 而是可以将数组分割成小于V, 等于V, 大于V的三部分, 然后对小于V和大于V的两部分在进行递归排序

伪代码

package com.paal.demo.c01SortingBasic.optimize;

import com.paal.demo.Sort;
import com.paal.demo.utils.SortTestHelper;

/**
 * <p/>
 * <li>title: 快速排序优化</li>
 * <li>@author: li.pan</li>
 * <li>Date: 2019/12/10 22:50 下午</li>
 * <li>Version: V1.0</li>
 * <li>Description:
 * 优化1: 对于小规模数组, 使用插入排序,效率更佳
 * 优化2: 对于arr[l...r]可能近乎有序的数组,采用获取随机值的方式随机获取基准值位置
 * 优化3: 对于arr[l...r]可能存在大量元素的数组,可以将数组分割为小于V,等于V,大于V的三部分,然后对小于V和大于V部分进行递归
 * </li>
 */
public class QuickSortOptimize implements Sort {

    /**
     * 快速排序入口
     *
     * @param arr 数组
     */
    @Override
    public void sort(Integer[] arr) {

        int n = arr.length;
        sort(arr, 0, n - 1);
    }

    /**
     * 递归使用快速排序,对arr[l...r]的范围进行排序
     *
     * @param arr 无序集合
     * @param l   低索引
     * @param r   高索引
     */
    private static void sort(Integer[] arr, int l, int r) {

        /**
         *  优化1: 对于小规模数组, 使用插入排序,效率更佳
         */
        if (r - l <= 15) {
            insertionSort(arr, l, r);
            return;
        }

        /**
         * 优化2: 对于arr[l...r]可能近乎有序的数组,采用获取随机值的方式随机获取基准值位置
         * (int) (Math.random() * (r - l + 1)) + l
         */
        SortTestHelper.swap(arr, l, (int) (Math.random() * (r - l + 1)) + l);

        Integer v = arr[l];

        /**
         * 优化3: 对于arr[l...r]可能存在大量元素的数组,可以将数组分割为小于V,等于V,大于V的三部分,然后对小于V和大于V部分进行递归
         * 简称"三路快排"
         */
        int lt = l;     // arr[l+1...lt] < v
        int gt = r + 1; // arr[gt...r] > v
        int i = l + 1;    // arr[lt+1...i) == v
        while (i < gt) {
            if (arr[i] < v) {
                SortTestHelper.swap(arr, i, lt + 1);
                i++;
                lt++;
            } else if (arr[i].compareTo(v) > 0) {
                SortTestHelper.swap(arr, i, gt - 1);
                gt--;
            } else { // arr[i] == v
                i++;
            }
        }
        SortTestHelper.swap(arr, l, lt);

        sort(arr, l, lt - 1);
        sort(arr, gt, r);
    }

    // 对arr[l...r]的区间使用InsertionSort排序
    public static void insertionSort(Integer[] arr, int l, int r) {

        for (int i = l + 1; i <= r; i++) {
            Integer e = arr[i];
            int j = i;
            for (; j > l && arr[j - 1] > e; j--)
                arr[j] = arr[j - 1];
            arr[j] = e;
        }
    }
}

    /**
     * 快速排序测试优化方法
     */
    @Test
    public void quickOptimizeSortTest() {

        Integer[] integers0 = SortTestHelper.generateRandomArray(100, 0, 1000000);
        Integer[] integers1 = SortTestHelper.generateRandomArray(10000, 0, 1000000);
        Integer[] integers2 = SortTestHelper.generateRandomArray(100000, 0, 1000000);
        System.out.println("------------------------------随机数组--------------------------------");
        System.out.println("插入排序测试1数据量为100" + SortTestHelper.testSort(integers0, new InsertionSort()));
        System.out.println("插入排序测试2数据量为10000" + SortTestHelper.testSort(integers1, new InsertionSort()));
        System.out.println("插入排序测试3数据量为100000" + SortTestHelper.testSort(integers2, new InsertionSort()));
        System.out.println("归并排序优化测试1数据量为100" + SortTestHelper.testSort(integers0, new MergeSortOptimize()));
        System.out.println("归并排序优化测试2数据量为10000" + SortTestHelper.testSort(integers1, new MergeSortOptimize()));
        System.out.println("归并排序优化测试3数据量为100000" + SortTestHelper.testSort(integers2, new MergeSortOptimize()));
        System.out.println("快速排序测试1数据量为100" + SortTestHelper.testSort(integers0, new QuickSort()));
        System.out.println("快速排序测试2数据量为10000" + SortTestHelper.testSort(integers1, new QuickSort()));
        System.out.println("快速排序测试3数据量为100000" + SortTestHelper.testSort(integers2, new QuickSort()));
        System.out.println("快速排序测试1数据量为100" + SortTestHelper.testSort(integers0, new QuickSortOptimize()));
        System.out.println("快速排序测试2数据量为10000" + SortTestHelper.testSort(integers1, new QuickSortOptimize()));
        System.out.println("快速排序测试3数据量为100000" + SortTestHelper.testSort(integers2, new QuickSortOptimize()));

        System.out.println("------------------------------近乎有序的数组--------------------------------");
        Integer[] integers00 = SortTestHelper.generateNearlyOrderedArray(100, 10);
        Integer[] integers11 = SortTestHelper.generateNearlyOrderedArray(10000, 100);
        Integer[] integers22 = SortTestHelper.generateNearlyOrderedArray(100000, 1000);
        System.out.println("插入排序测试1数据量为100" + SortTestHelper.testSort(integers00, new InsertionSort()));
        System.out.println("插入排序测试2数据量为10000" + SortTestHelper.testSort(integers11, new InsertionSort()));
        System.out.println("插入排序测试3数据量为100000" + SortTestHelper.testSort(integers22, new InsertionSort()));
        System.out.println("归并排序优化测试1数据量为100" + SortTestHelper.testSort(integers00, new MergeSortOptimize()));
        System.out.println("归并排序优化测试2数据量为10000" + SortTestHelper.testSort(integers11, new MergeSortOptimize()));
        System.out.println("归并排序优化测试3数据量为100000" + SortTestHelper.testSort(integers22, new MergeSortOptimize()));
        System.out.println("快速排序测试1数据量为100" + SortTestHelper.testSort(integers00, new QuickSort()));
        System.out.println("快速排序测试2数据量为10000" + SortTestHelper.testSort(integers11, new QuickSort()));
        System.out.println("快速排序测试3数据量为100000" + SortTestHelper.testSort(integers22, new QuickSort()));
        System.out.println("快速排序测试1数据量为100" + SortTestHelper.testSort(integers00, new QuickSortOptimize()));
        System.out.println("快速排序测试2数据量为10000" + SortTestHelper.testSort(integers11, new QuickSortOptimize()));
        System.out.println("快速排序测试3数据量为100000" + SortTestHelper.testSort(integers22, new QuickSortOptimize()));

        Integer[] integers000 = SortTestHelper.generateRandomArray(100, 0, 10);
        Integer[] integers111 = SortTestHelper.generateRandomArray(10000, 0, 100);
        Integer[] integers222 = SortTestHelper.generateRandomArray(100000, 0, 1000);
        System.out.println("------------------------------大量重复的数组--------------------------------");
        System.out.println("插入排序测试1数据量为100" + SortTestHelper.testSort(integers000, new InsertionSort()));
        System.out.println("插入排序测试2数据量为10000" + SortTestHelper.testSort(integers111, new InsertionSort()));
        System.out.println("插入排序测试3数据量为100000" + SortTestHelper.testSort(integers222, new InsertionSort()));
        System.out.println("归并排序优化测试1数据量为100" + SortTestHelper.testSort(integers000, new MergeSortOptimize()));
        System.out.println("归并排序优化测试2数据量为10000" + SortTestHelper.testSort(integers111, new MergeSortOptimize()));
        System.out.println("归并排序优化测试3数据量为100000" + SortTestHelper.testSort(integers222, new MergeSortOptimize()));
        System.out.println("快速排序测试1数据量为100" + SortTestHelper.testSort(integers000, new QuickSort()));
        System.out.println("快速排序测试2数据量为10000" + SortTestHelper.testSort(integers111, new QuickSort()));
        System.out.println("快速排序测试3数据量为100000" + SortTestHelper.testSort(integers222, new QuickSort()));
        System.out.println("快速排序测试1数据量为100" + SortTestHelper.testSort(integers000, new QuickSortOptimize()));
        System.out.println("快速排序测试2数据量为10000" + SortTestHelper.testSort(integers111, new QuickSortOptimize()));
        System.out.println("快速排序测试3数据量为100000" + SortTestHelper.testSort(integers222, new QuickSortOptimize()));

    }

执行结果

------------------------------随机数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.001s
插入排序测试2数据量为10000排序时长为: 0.08s
插入排序测试3数据量为100000排序时长为: 5.66s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.005s
归并排序优化测试3数据量为100000排序时长为: 0.003s
快速排序测试1数据量为100排序时长为: 0.0s
快速排序测试2数据量为10000排序时长为: 0.045s
快速排序测试3数据量为100000排序时长为: 6.748s
快速排序测试1数据量为100排序时长为: 0.0s
快速排序测试2数据量为10000排序时长为: 0.004s
快速排序测试3数据量为100000排序时长为: 0.033s
------------------------------近乎有序的数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.0s
插入排序测试2数据量为10000排序时长为: 0.001s
插入排序测试3数据量为100000排序时长为: 0.097s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.0s
归并排序优化测试3数据量为100000排序时长为: 0.0s
快速排序测试1数据量为100排序时长为: 0.0s
快速排序测试2数据量为10000排序时长为: 0.031s
快速排序测试3数据量为100000排序时长为: 3.459s
快速排序测试1数据量为100排序时长为: 0.0s
快速排序测试2数据量为10000排序时长为: 0.0s
快速排序测试3数据量为100000排序时长为: 0.008s
------------------------------大量重复的数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.0s
插入排序测试2数据量为10000排序时长为: 0.039s
插入排序测试3数据量为100000排序时长为: 5.454s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.0s
归并排序优化测试3数据量为100000排序时长为: 0.0s
快速排序测试1数据量为100排序时长为: 0.0s
快速排序测试2数据量为10000排序时长为: 0.034s
快速排序测试3数据量为100000排序时长为: 7.021s
快速排序测试1数据量为100排序时长为: 0.0s
快速排序测试2数据量为10000排序时长为: 0.001s
快速排序测试3数据量为100000排序时长为: 0.005s
发布了87 篇原创文章 · 获赞 69 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/lp284558195/article/details/103486398