轻松拿下快速排序

快速排序!拿下!!!

快速排序是现在编程语言自带排序函数中,使用的最多的算法

例如常见的:List.sort()

同时,也是面试最喜欢面试的排序算法,没有之一

实现的原理、复杂度都有一定的难度

接下来我们来看看快速排序的实现原理:

在快速排序中,首先我们会随机选中一个数字当作,目标是将原始数组根据轴进行分区分拆,比轴小的放在左边,比轴大的放在右边。那么该如何做到这一点呢?如下:

首先,给定一个原始数组 1, 6, 4, 2, 5, 3。第一步,我们选择哪个数字为轴呢?理论上选择任意值都可以,在这里先选择最后一个元素 3 作为轴

选择轴本身有相应的技巧和方法,此处只考虑简单的情况

紧接着,我们放置左右两个指针,分别指向索引 0 和索引倒数第二个,如图所示:

图片选择优课达官网

接下来将除了3以外的数字进行分区,分配到小于3和大于3两个部分

逻辑为:

  1. 左指针依次往右侧移动,当遇到大于或者等于 3 的数值时停止
  2. 右指针依次往左侧移动,当遇到小于或者等于 3 的数值时停止
  3. 将左右两个指针指向的值互换
  4. 互换以后继续重复上面三个步骤,直到左右两个指针重合
  5. 将左右两个指针指向的值和轴 3 互换

具体步骤如下图,首先执行1、2两步:

在这里插入图片描述

如图:交换了 6 和 2 的位置

接下来继续推进左右指针,发现左右指针在4上重合,所以接下来执行步骤 5,让 4 和 3 交换,如下图所示:

在这里插入图片描述

此时,轴 3 已经在正确的位置上,即使3的左右仍是乱序

通过第一次分区,我们将数组分为左右两个部分,按照递归的思想,我们继续将左右两侧数组按照选轴分区重复排序。下面开始第二次分区:

在这里插入图片描述

综上不难发现:快速排序的宗旨其实就是通过每次轴分区,都能完成轴的位置定位

时间复杂度

快速排序每一次分区都需要整个数组进行依次比较,时间复杂度为 O(N)。按照二分法排序一共应该需要 log(N) 次分区,所以最终结果为:O(Nlog(N))

但是存在一种最坏的情况,就是元素的本身都是有序的,那么会发生如图所示这种情况:

在这里插入图片描述

这样下去,每次分区其实相当于没有分区,因为轴永远是最大的值,这种情况下的时间复杂度是 O(N^2)。但是这种情况一般不会出现,只需要了解即可~

代码实现快速排序

将算法用代码实现的时候,需要特别注意总分的思想,不能一开始就去死抠细节。比如,可以思考一下算法逻辑一共可以分为几个大块。首先,快速排序是一个递归思维,既然是递归,就需要知道递归的基准条件和递归公式是什么

基准条件:当数组里元素小于等于1个元素的时候结束

递归公式:当每一次分区,获取到轴位置后,左右分拆数组,继续快速排序

public class QuickSort {

    // 快速排序
    public static void quickSort(int[] array) {
        // 调用快速排序的核心,传入left,right
        quickSortCore(array, 0, array.length - 1);
    }

    // 快速排序的核心其实就是递归函数
    public static void quickSortCore(int[] array, int left, int right) {
        // 递归基准条件,left >= right 即表示数组只有1个或者0个元素
        if (left >= right) {
            return;
        }
        // 根据轴分区
        int pivotIndex = partition(array, left, right);

        // 递归调用左侧和右侧数组分区
        quickSortCore(array, left, pivotIndex - 1);
        quickSortCore(array, pivotIndex + 1, right);
    }

    // 对数组进行分区,并返回当前轴所在的位置
    public static int partition(int[] array, int left, int right) {
        int pivot = array[right];

        int leftIndex = left;
        int rightIndex = right - 1;
        while (true) {
            // 左指针移动
            while (array[leftIndex] <= pivot && leftIndex < right) {
                leftIndex++;
            }
            // 右指针移动
            while (array[rightIndex] >= pivot && rightIndex > 0) {
                rightIndex--;
            }

            if (leftIndex >= rightIndex) {
                break;
            } else {
                swap(array, leftIndex, rightIndex);
            }
        }

        swap(array, leftIndex, right);
        return leftIndex;
    }

    public static void swap(int[] array, int index1, int index2) {
        int temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
    }
    
}

小贴士:有了 quickSortCore 方法还使用 quickSort 方法的原因

因为为了方便外部程序调用排序,只需要传入数组就可以了,并不需要再传入 leftright。这就是接口友好的一种设计表现

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值