【算法】快速排序绝对要会!

136 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

我认为写算法类文章的几个行文要素:

  1. 专注核心概念,不做过多延伸
  2. 一遍也能看懂
  3. 应用!应用!应用! 总的来说就是不要说太多废话,也不要太惜字!

梳理快排的概念💎

一种将无需数组排列成有序数组的方式。

先从无序数组中找出一个基准数,然后遍历一遍数组,将小于基准数的元素放在基准数左边,大于的放基准数右边。这样的一次遍历就能够确定基准数在这个数组中的最终位置。(因为有序数组中基准数左边也是都小于基准数的元素,基准书右边都是大于基准数的元素)

然后将基准数右边形成的一半数组序列再定一个基准数,再次遍历,确定新基准数的位置。 旧基准数左边形成的一半数组序列也遍历一次,确定一个新基准数的位置。

这样一直往下分别遍历,就能最终确定所有数的位置。

必须记住的快排特点❤❤❤❤❤

  1. 因为每次遍历用到的方法都一样,所以肯定是递归的思想。
  2. 快排是不稳定的,也就是快排后的有序数组中,原来数据相同的元素之间次序可能会发生改变。
  3. 理想快排的时间复杂度是:O(nlog2n)O(nlog_{2}n)

怎么写快排

1.搭框架

使用java语言,用到两个函数,一个main函数,一个专门用来快排的方法函数。

重点来写方法函数,起个名字叫QuickSortMethod(), 首先肯定得传递要排序的数组arr这个参数。

而且由于递归思想,我们这个函数要递归调用,所以要多出两个参数——所传入数组的上下标,用来确定我们要排序的是数组的哪一个部分。

先不写方法中具体排序方式,而是分析框架:

  1. 基准数?就用数组第一个元素。也就是i指针指向的元素
  2. 用什么遍历?定义两个指针变量i=0,j=arr.length-1,
  3. 怎么遍历?
    • 先从后向前让每个元素值arr[j]与基准数arr[i]比较,不断的让j--,直到找到第一个小于基准的元素值arr[j],让基准arr[i]和arr[j]的数值交换。现在基准值到了arr[j]上。
    • 再从前向后,从arr[i]开始找,不断的让i++,直到找到第一个大于基准arr[j]的元素值arr[i],两个数交换位置。现在基准值到了arr[i]
    • 重复前两步
  4. 什么时候停止遍历?
    • i不断增大,j不断减小,到i=j的时候,基准值的位置就确定在这里了。
  5. 怎么重启遍历
    • 分别对基准数左右两半的数组序列调用快排方法QuickSortMethod()

忽略第三步里具体的遍历步骤(我们下面再细讲),大致的代码框架就是下面这样:

import java.util.Arrays;

public class QuickSort {
    //主函数做测试
    public static void main(String[] args) {
        int[] arr = {5,7,10,48,3,16,56,20,70,32,90,100,1,88,4};
        QuickSortMethod(arr,0,arr.length-1);    //调用快排方法
        System.out.println(Arrays.toString(arr));   //输出排序后的数组
    }
    //快速排序的方法,传递三个参数,数组名,数组开始下标,数组结束下标
    private static void QuickSortMethod(int[] arr, int low, int high) {
        //递归结束条件,就是当low>=high时,说明要排序的数组序列已经被细分到个体,可以结束递归了
        if(low>=high){
            return;
        }
        //定义两个移动数组下标的指针
        int i = low;
        int j = high;
        //操作指针,这次循环是为了确定一个基准数的位置
        while(i<j){
            
        }
        //当跳出循环,说明一次快排结束,需要对分成的两个数组部分分别进行快排
        QuickSortMethod(arr,low,j-1);
        QuickSortMethod(arr,j+1,high);
    }
}

2.写具体方法

接下来,我们丰富while语句内的代码,必须注意这里的while语句就是为了不断的让i++,j--,直到i和j相等,说明我们找到了该次遍历的终点:基准数的正确位置!

要做的就是罗列出来的这段话:

  1. 怎么遍历?
  • 先从后向前让每个元素值arr[j]与基准数arr[i]比较,不断的让j--,直到找到第一个小于基准的元素值arr[j],让基准arr[i]和arr[j]的数值交换。现在基准值到了arr[j]上。
  • 再从前向后,从arr[i]开始找,不断的让i++,直到找到第一个大于基准arr[j]的元素值arr[i],两个数交换位置。现在基准值到了arr[i]
  • 重复前两步

先确定,现在的基准数默认在arr[i]上,也就是数组的第一个元素上,我们从后往前,不断让j--,“不断”两字说明这里又用到一个循环语句,才能让j不断的--,直到发现arr[j]小于基准数时,停止,当然如果arr[j]一直都大于基准数,j就一直--到和i相等的时候,我们也要停止。

停止后就可以交换基准数和arr[i]所在的位置,也就是交换arr[j]和arr[i]的值。交换两数值这个动作很常见,所以就专门定义了一个函数,用到的时候直接调用了。

交换完,我们就可以从前往后遍历,让i不断++,停止i++的时候就是我们交换值的时候!

这个过程转换为代码就是这样:

while(i<j){
    //从后往前比较,如果arr【j】< arr【i】,就交换两个的位置
    while(i<j && arr[j]>arr[i]){
        j--;
    }
    swap(arr,i,j);//用来交换arr[i]和arr[j]的值
    //从前往后比较,如果arr【i】> arr【j】(基准),交换位置
    while(i<j && arr[i]<arr[j]){
        i++;
    }
    swap(arr,i,j);//用来交换arr[i]和arr[j]的值
}

运行主函数来测试一下:

image.png

练习题:

leetcode: 912. 排序数组 - 力扣(LeetCode) (leetcode-cn.com)

辅助资料

东软睿道-3小时学透十大经典排序算法_哔哩哔哩_bilibili

快速排序的正确理解方式及运用 (qq.com)