神级基础排序——快速排序

概念

快速排序是一种原地排序,只需要一个很小的栈作为辅助空间,空间复杂度为O(logN),所以适合在数据集比较大且无序的时候使用。实现方法有经典快排和双指针快排,本文介绍的是双指针快排的实现。

【注意】:快排的经典排序实现的partition过程其实就是荷兰国旗问题,不明白的或者不熟悉的可以先看一下荷兰国旗算法。本文的实现主要讲双指针法。
荷兰国旗算法:荷兰国旗问题&快排&BFPRT算法 - god-jiang的文章 - 知乎

时间复杂度

时间复杂度比较复杂,最好的情况是O(N),最差的时候是O(N^2),所以平时说的O(N*logN)为其平均时间复杂度。

基本思想

随机找出一个数,可以随机取,也可以取固定位置,一般是取第一个或最后一个称为基准,然后就是比基准小的放在左边,比基准大的放到右边。如何放呢?就是和基准进行交换,这样交换完左边都是比基准小的,右边都是比较基准大的,这样就将一个数组分成了两个子数组,然后再按照同样的方法把子数组再分成更小的子数组,直到不能分解为止。

操作实现

partition方法中

1.选择数组中的第一个元素arr[startIndex]作为轴(pivot)

2.右指针为right,从最右面的一个元素开始向左寻找第一个小于等于pivot的数值

3.左指针为left,从最左边开始寻找第一个比pivot大的数

4.经过2,3两个步骤后,将会出现以下两种情况
​ (1):left和right没有相遇,此时进行交换,swap(arr,left,right);
​ (2):left和right相遇,做swap(arr,startIndex,left),然后返回left

5.partition中返回pivot用于分割数组,下一次用于排序的数组被分割为(startIndex,pivot-1),(pivot+1,endIndex)两段,进行递归操作
过程演示

给定一个原始数组为:
在这里插入图片描述

开始时,我们首先选择第一个数作为基准pivot,并且设置两个指针left和right,指向数组的最左位置和最右位置:
在这里插入图片描述

从right位置开始和基准pivot比较,直到right对应的那个数比基准pivot小的时候停下;然后从left位置开始和基准pivot比较,直到left对应的那个数比基准pivot大的时候停下。因为right对应的1比4小,所以停下不动;left对应的是4没有大于4,所以向右移动,然后left对应的是7比4大,所以停下不动:
在这里插入图片描述

这时交换left和right的位置:
在这里插入图片描述

接下来继续重复刚才的过程,right向左移动到8比4大,继续移动到2,比4小,所以停下;然后left向右移动到6比4大,所以停下:
在这里插入图片描述

这时交换left和right的位置:
在这里插入图片描述

继续重复上面的过程,right向左移动到3比4小,所以停下;left向右移动到5比4大,所以停下:
在这里插入图片描述

这时交换left和right的位置:
在这里插入图片描述

right向左移动和left指针重合,这个时候停下:
在这里插入图片描述

当left和right重合的时候,我们把pivot位置和left位置进行交换,这个时候就是一趟快排的结果,基准pivot的值是4,它的左边都比4小,它的右边都比4大:
在这里插入图片描述

然后就通过分治递归的方法,分别对左边和右边继续刚才的过程,直到数组不能再分为止。

代码实现

import java.util.Arrays;
/**
 * @author god-jiang
 * @date 2020/1/12
 */
//时间复杂度O(n*logn),空间复杂度O(n*logn)
public class QuickSort {
    public static void quickSort(int[] arr, int startIndex, int endIndex) {
        if (startIndex < endIndex) {
            //找出基准
            int partition = partition(arr, startIndex, endIndex);
            //分成两边递归进行
            quickSort(arr, startIndex, partition - 1);
            quickSort(arr, partition + 1, endIndex);
        }
    }

    //找基准
    private static int partition(int[] arr, int startIndex, int endIndex) {
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;
        while (left != right) {
            while (left < right && arr[right] > pivot) {
                right--;
            }
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            //找到left比基准大,right比基准小,进行交换
            if (left < right) {
                swap(arr, left, right);
            }
        }
        //第一轮完成,让left和right重合的位置和基准交换,返回基准的位置
        swap(arr, startIndex, left);
        return left;
    }

    //两数交换
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] a = {2, 4, 6, 1, 3, 7, 9, 8, 5};
        quickSort(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));
    }
}

运行截图

在这里插入图片描述

1、以上就是今天分享的快速排序,时间复杂度为O(NlogN),空间复杂度为O(NlogN)
2、快排是不稳定排序,要想做到稳定性是可以的,但是非常难,不需要掌握,有一篇论文叫”01 stable sort”可以做到
3、有一道题目,是奇数放数组左边,偶数放在数组右边,还要求原始的相对次序不变,额外空间复杂度为O(1),碰到这个问题,直接可以怼面试官,根本不可能做出来

PS:因为博主画图实在太丑了,所以去了“程序员小灰”的公众号copy了他的图片来讲解快排的整个过程。觉得博主写的还不错的点点赞,关注走一波,谢谢大家的支持了,我们一起进步吧~~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值