动画:一篇文章快速学会快速排序

内容介绍

快速排序简介

快速排序(Quicksort)是对冒泡排序的一种改进。快速排序由C. A. R. Hoare在1960年提出。快速排序算法被列为20世纪十大算法之一,这足以说明的他的作用和重要性。快速排序是程序员必须掌握的一种排序算法。

希尔排序相当于直接插入排序的升级,它们同属于插入排序类,快速排序其实就是我们前面认为最慢的冒泡排序的升级,它们都属于交换排序类。它也是通过不断比较和移动交换来实现排序的,只不过它的实现,增大了记录的比较和移动的距离,快速排序会取一个分界值,将比分界值大的记录从前面直接移动到后面,比分界值小的记录从后面直接移动到前面,从而减少了总的比较次数和移动交换次数。

快速排序的思想

快速排序的思想:取一个分界值,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比分界值小,另外一部分的所有数据比分界值大,然后再按此方法对这两部分数据分别进行相同操作,整个排序过程可以递归进行,最终达到整个数据变成有序序列。

快速排序动画演示

快速排序分析

一般没有特殊要求排序算法都是升序排序,小的在前,大的在后。
数组由{5, 3, 1, 9, 7, 2, 8, 6} 这8个无序元素组成。

快速排序步骤:

  1. 取一个分界值:我们暂且拿待排序数据的最前一个元素作为分界值(枢轴)。

  2. 分区,low到high之间的元素分成左边小于枢轴,最右边大于枢轴。

  3. 分区后小于枢轴和大于枢轴的两个区域再进行分区,依次类推直到每个分区数据满足左边小于枢轴,右边大于枢轴,排序完成。

最终结果,如下图:

快速排序需要解决的两个问题:

  1. 分区后还需要分区可以使用递归。
  2. 分区时如何让小于枢轴的数据放到枢轴左边,大于枢轴的数据放到枢轴的右边。
    使用两个指针(i, j),i是用来找小于枢轴的数据,j是用来找大于枢轴的数据。
    i. 循环查找到需要换位置的数据,进行换位置。
    ii. 当i索引的数据大于枢轴,这个数据需要换位置,记录i的值,停止查找。
    iii. 当j索引的数据小于枢轴,这个数据需要换位置,记录j的值,停止查找。
    iv. 如果i > j说明已经找完了,退出循环。
    v. 让i和j位置的元素换位置,i++,指针向右移动继续找大于枢轴的数据,j–向左移动继续找小于枢轴的数据。过程如下动画所示:

快速排序代码编写

代码说明:

  1. void quickSort(int[] arr)方法:用于快速排序的方法,参数为需要排序的数组。
  2. void qSort(int[] arr, int low, int high)方法:用于将数组指定范围的数据进行快速排序,此方法不暴露给用户使用。
  3. int partition(int[] arr, int low, int high)方法:快速排序的分区,将low到high之间的元素分成左边小于枢轴,最右边大于枢轴,返回枢轴的位置。
  4. void swap(int[] arr, int start, int end)方法:将arr数组start索引和end索引的元素进行交换位置。

快速排序代码如下:

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

        System.out.println("排序后:" + Arrays.toString(arr));
    }

    public static void quickSort(int[] arr) {
   
        qSort(arr, 0, arr.length-1);
    }

    // 对arr数组的[low, right]部分进行快速排序
    private static void qSort(int[] arr, int low, int high) {
   
        if (low >= high) return;
        // 快速排序的分区,将low到high之间的元素分成左边小于枢轴,最右边大于枢轴
        int pivot = partition(arr, low, high);

        // 再次对枢轴左边和右边的数据进行分区。
        qSort(arr, low, pivot - 1);
        qSort(arr,pivot + 1, high);
    }

    // 快速排序的分区,将low到high之间的元素分成左边小于枢轴,最右边大于枢轴,返回枢轴的位置。
    private static int partition(int[] arr, int low, int high) {
   
        // 将第一个元素作为枢轴
        int v = arr[low];

        int i = low + 1; // arr[low+1, i) <= v; arr(j, high] >= v
        int j = high;

        while (true) {
   
            // 从左边找到大于枢轴的数据
            while (i <= high && arr[i] < v) {
   
                i++;
            }

            // 从右边找到小于枢轴的数据
            while (j >= low+1 && arr[j] > v) {
   
                j--;
            }
            if (i > j) break;

            swap(arr, i, j); // 交换i和j位置的元素
            i++; // 左边的指针向右移动继续找大于枢轴的数据
            j--; // 右边的指针向左移动继续找小于枢轴的数据
        }

        // 交换枢轴到j索引,保证枢轴左边的元素小于枢轴,枢轴右边元素大于枢轴。
        swap(arr, low, j);
        return j;
    }

    // 数组两个元素交换
    public static void swap(int[] arr, int start, int end) {
   
        if (start == end)
            return;

        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
    }
}

快速排序代码优化1

优化枢轴的选取

我们知道快速会不断对数据进行分区,选定一个枢轴,将小于枢轴的数据放到左边,大于枢轴的数据放到右边。

前面我们在对数据进行分区时,都是以数组最前面一个元素作为枢轴,枢轴的选取不够合理。这样会存在一个问题,当数据本身近乎有序时比如数据为:{1, 2, 3, 5, 6, 7, 9, 8},分区时选择最左边的数据作为枢轴,恰好是数组最小或最大数据,导致分区时,数据都在数轴一侧会导致快速排序退化为一个O(n2)的算法。

如何选取枢轴才不会让近乎有序的数据排序退化成O(n^2)呢,我们可以看到原因是我们一直选取数组最前面的一个数据作为枢轴,因此我们可以随机选取一个元素作为数轴,这样,每次都选取到最大或最小的概率就会非常低。改进后的代码如下:

public class QuickSortTest2 {
   
    public static void main(String[] args) {
   
        int[] arr = new int[] {
   5, 3, 1, 9,
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值